Introduction

Airbnb, Inc (Airbnb) adalah marketplace untuk memesan penginapan/homestay atau paket liburan, secara online melalui aplikasi. Pengguna cukup mengunduh aplikasi Airbnb melalui Google Play/Apple Store, kemudian pengguna akan mencari lokasi properti penginapan, berikut tanggal menginap. Aplikasi kemudian akan menampilkan daftar properti yang tersedia pada lokasi dan tanggal tersebut. Pengguna kemudian membayarkan fee kepada Airbnb, untuk diteruskan kepada pemilik properti. Dalam hal ini, perusahaan Airbnb tidak memiliki aset properti yang ditawarkan, namun Airbnb bertindak sebagai perantara (antara pemilik properti [host] dan pengguna). Airbnb berkantor pusat di San Fransisco, California, Amerika Serikat.

Dari properti yang tersedia di Airbnb, apakah faktor penentu harga properti di Airbnb?

Methodology

Data Understanding

SteveZheng (Steve Zheng), merupakan kontributor di Kaggle.com yang mengunggah dataset “Airbnb price prediction”. Dataset merupakan data properti Airbnb yang ada di Amerika Serikat. Dataset terdiri dari 74111 baris data, dengan 29 fitur

Deskripsi fitur-fitur yang tersedia (menurut interpretasi penulis, dikarenakan tidak adanya dokumentasi resmi dari kontributor) dan yang digunakan, adalah sebagai berikut :

No Feature Description Fitur digunakan? Response/Predictor
1 id Nomor ID properti Tidak -
2 log_price Harga sewa properti (dalam bentuk log) Ya Response
3 property_type Jenis properti (contoh : apartemen, rumah, dsb) Ya Predictor
4 room_type Jenis kamar yang disewakan. Contoh : 1) entire home/apt –> seluruh rumah/apartemen, 2) private room –> kamar pribadi yang disewakan, 3) Shared room –> kamar yang dibagi dengan pengguna lain) Ya Predictor
5 amenities Kelengkapan lain dari properti Tidak -
6 accomodates Jumlah kapasitas orang yang dapat menginap dalam properti yang disewakan Ya Predictor
7 bathrooms Jumlah kamar mandi tersedia Ya Predictor
8 bed_type Jenis kasur yang disediakan : Real Bed, Futon, Pull-out Sofa, Couch atau Airbed Ya Predictor
9 cancellation_policy Persyaratan pembatalan Ya Predictor
10 cleaning_fee Ada atau tidaknya biaya pembersihan kamar Ya Predictor
11 city Kota letak properti berada Ya Predictor
12 description Deskripsi properti Tidak -
13 first_review Tanggal review pertama Ya Predictor
14 host_has_profile_pic Penanda apakah host menampilkan foto profil pada akun Airbnb Tidak -
15 host_identity_verified Penanda apakah host telah diverifikasi oleh Airbnb Tidak -
16 host_response_rate Kecepatan host dalam merespons setiap pertanyaan yang masuk Tidak -
17 host_since Tanggal host mulai memasukkan propertinya pada Airbnb Tidak -
18 instant_bookable Penanda apakah properti dapat langsung di book tanpa perlu adanya konfirmasi dari host Ya Predictor
19 last_review Tanggal review terakhir Tidak -
20 latitude Latitude lokasi properti Ya Predictor
21 longitude Longitude lokasi properti Ya Predictor
22 name Nama properti Tidak -
23 neighbourhood nama wilayah Tidak -
24 number_of_reviews jumlah review yang diberikan Tidak -
25 review_scores_rating Skor review Ya Predictor
26 thumbnail_url URL link gambar dari properti Tidak -
27 zipcode Kodepos properti Tidak -
28 bedrooms Banyak kamar tidur Ya Predictor
29 beds Banyak tempat tidur yang disediakan Ya Predictor

Adapun ringkasan dari nilai pada setiap feature adalah sebagai berikut :

library(tidyverse)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
-- Attaching packages --------------------------------------- tidyverse 1.2.1 --
v ggplot2 3.2.1     v purrr   0.3.2
v tibble  2.1.3     v dplyr   0.8.3
v tidyr   1.0.0     v stringr 1.4.0
v readr   1.3.1     v forcats 0.4.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
airBNB<-read_csv("train.csv")
Parsed with column specification:
cols(
  .default = col_character(),
  id = col_double(),
  log_price = col_double(),
  accommodates = col_double(),
  bathrooms = col_double(),
  cleaning_fee = col_logical(),
  first_review = col_date(format = ""),
  host_has_profile_pic = col_logical(),
  host_identity_verified = col_logical(),
  host_since = col_date(format = ""),
  instant_bookable = col_logical(),
  last_review = col_date(format = ""),
  latitude = col_double(),
  longitude = col_double(),
  number_of_reviews = col_double(),
  review_scores_rating = col_double(),
  bedrooms = col_double(),
  beds = col_double()
)
See spec(...) for full column specifications.
summary(airBNB)
       id             log_price     property_type       room_type        
 Min.   :     344   Min.   :0.000   Length:74111       Length:74111      
 1st Qu.: 6261964   1st Qu.:4.317   Class :character   Class :character  
 Median :12254147   Median :4.710   Mode  :character   Mode  :character  
 Mean   :11266617   Mean   :4.782                                        
 3rd Qu.:16402260   3rd Qu.:5.220                                        
 Max.   :21230903   Max.   :7.600                                        
                                                                         
  amenities          accommodates      bathrooms       bed_type        
 Length:74111       Min.   : 1.000   Min.   :0.000   Length:74111      
 Class :character   1st Qu.: 2.000   1st Qu.:1.000   Class :character  
 Mode  :character   Median : 2.000   Median :1.000   Mode  :character  
                    Mean   : 3.155   Mean   :1.235                     
                    3rd Qu.: 4.000   3rd Qu.:1.000                     
                    Max.   :16.000   Max.   :8.000                     
                                     NA's   :200                       
 cancellation_policy cleaning_fee        city           description       
 Length:74111        Mode :logical   Length:74111       Length:74111      
 Class :character    FALSE:19708     Class :character   Class :character  
 Mode  :character    TRUE :54403     Mode  :character   Mode  :character  
                                                                          
                                                                          
                                                                          
                                                                          
  first_review        host_has_profile_pic host_identity_verified
 Min.   :2008-11-17   Mode :logical        Mode :logical         
 1st Qu.:2015-06-28   FALSE:226            FALSE:24175           
 Median :2016-05-25   TRUE :73697          TRUE :49748           
 Mean   :2016-01-14   NA's :188            NA's :188             
 3rd Qu.:2017-01-02                                              
 Max.   :2017-10-05                                              
 NA's   :15864                                                   
 host_response_rate   host_since         instant_bookable  last_review        
 Length:74111       Min.   :2008-03-03   Mode :logical    Min.   :2009-01-21  
 Class :character   1st Qu.:2013-04-21   FALSE:54660      1st Qu.:2017-01-09  
 Mode  :character   Median :2014-09-28   TRUE :19451      Median :2017-04-28  
                    Mean   :2014-07-21                    Mean   :2017-03-14  
                    3rd Qu.:2015-12-22                    3rd Qu.:2017-09-08  
                    Max.   :2017-10-04                    Max.   :2017-10-05  
                    NA's   :188                           NA's   :15827       
    latitude       longitude           name           neighbourhood     
 Min.   :33.34   Min.   :-122.51   Length:74111       Length:74111      
 1st Qu.:34.13   1st Qu.:-118.34   Class :character   Class :character  
 Median :40.66   Median : -77.00   Mode  :character   Mode  :character  
 Mean   :38.45   Mean   : -92.40                                        
 3rd Qu.:40.75   3rd Qu.: -73.95                                        
 Max.   :42.39   Max.   : -70.99                                        
                                                                        
 number_of_reviews review_scores_rating thumbnail_url        zipcode         
 Min.   :  0.0     Min.   : 20.00       Length:74111       Length:74111      
 1st Qu.:  1.0     1st Qu.: 92.00       Class :character   Class :character  
 Median :  6.0     Median : 96.00       Mode  :character   Mode  :character  
 Mean   : 20.9     Mean   : 94.07                                            
 3rd Qu.: 23.0     3rd Qu.:100.00                                            
 Max.   :605.0     Max.   :100.00                                            
                   NA's   :16722                                             
    bedrooms           beds       
 Min.   : 0.000   Min.   : 0.000  
 1st Qu.: 1.000   1st Qu.: 1.000  
 Median : 1.000   Median : 1.000  
 Mean   : 1.266   Mean   : 1.711  
 3rd Qu.: 1.000   3rd Qu.: 2.000  
 Max.   :10.000   Max.   :18.000  
 NA's   :91       NA's   :131     

Dari summary diatas, diketahui bahwa terdapat beberapa feature yang memiliki nilai NA. Fitur review_scores_rating memiliki NA yang cukup tinggi. Hal ini diakibatkan karena properti tersebut belum memiliki rating. Diduga bahwa properti tersebut adalah properti baru. Oleh karena itu, dataset akan dibagi menjadi 2 cluster, yakni dataset untuk properti baru (belum pernah dipesan) dan properti lama (pernah dipesan). Adapun instance yang memiliki nilai NA untuk setiap cluster nya akan diabaikan dan tidak digunakan untuk proses pemodelan.

Dari data terdapat instance yang nilai log_price nya adalah 0. Terkait hal ini, instance tersebut tidak akan dimasukkan kedalam proses modelling

airBNB<-subset(airBNB,log_price>0)

Korelasi data numerik dapat disajikan sebagai berikut :

Dari grafik diatas, dapat terlihat bahwa log_price berkorelasi kuat dengan fitur accomodates dan bathrooms. Semakin banyak penginap yang dapat diakomodir dan semakin banyak jumlah kamar mandi, maka semakin tinggi harga sewa properti tersebut. Dalam hal ini, tidak ada pengaruh jumlah review terhadap rating score.

Secara garis besar, data yang digunakan memiliki sebaran harga sebagai berikut :

ggplot(airBNB,aes(log_price))+geom_histogram(bins=20)+labs(title = "Distribusi Harga Properti", x="Harga (dalam fungsi log)", y="Jumlah Properti")

ggplot(airBNB,aes(x=NULL, y=log_price))+geom_boxplot()+labs(title = "Distribusi Harga Properti", y="Harga (dalam fungsi log)",x=NULL)

Dari gambar terlihat bahwa harga mayoritas berada pada rentang 4.3 - 5.2

Jumlah properti berdasarkan jenis nya, dapat ditampilkan dalam grafik sebagai berikut :

ggplot(airBNB,aes(property_type))+geom_bar()+theme(axis.text.x = element_text(angle = 90))+labs(title = "Jumlah Properti berdasarkan Jenis Properti", y="Jenis Properti",x="Jumlah Properti")+ scale_x_discrete(limits=c(unique(airBNB$property_type)))

Pada grafik diatas, terlihat bahwa dataset didominasi dengan properti berjenis Apartement (apartemen) dan House (rumah). Distribusi jumlah properti berdasarkan jumlah penginap dapat digambarkan sebagai berikut :

ggplot(airBNB,aes(accommodates))+geom_bar()+theme(axis.text.x = element_text(angle = 90))+labs(title = "Distribusi Jumlah Properti berdasarkan Jumlah Penginap yang bisa Ditampung", y="Jumlah Properti",x="Jumlah Penginap") + scale_x_discrete(limits=c(1:1:16))

Dari grafik tersebut, mayoritas properti dapat menampung maksimal 2 penginap. Terdapat beberapa properti yang dapat menampung sampai 16 penginap.

Berdasarkan jumlah kamar mandi yang tersedia pada setiap properti, dapat digambarkan dalam grafik distribusi sebagai berikut :

ggplot(airBNB,aes(bathrooms))+geom_bar()+theme(axis.text.x = element_text(angle = 90))+labs(title = "Distribusi Jumlah Properti berdasarkan Jumlah Kamar Mandi", y="Jumlah Properti",x="Jumlah Kamar Mandi") + scale_x_discrete(limits=seq(from = 0, to = 8, by = 0.5))

Dari grafik dapat ditunjukkan bahwa minimal sebuah properti memiliki 1 kamar mandi. Beberapa properti memiliki kelipatan 0.5, ini artinya bahwa properti tersebut hanya memiliki kloset dan wastafel, namun tidak memiliki bath tub “what does 1 1/2 bath means?”.

Berdasarkan kota, distribusi properti dapat digambarkan sebagai berikut :

ggplot(airBNB,aes(city))+geom_bar()+theme(axis.text.x = element_text(angle = 90))+labs(title = "Distribusi Jumlah Properti berdasarkan Kota", y="Jumlah Properti",x="Kota")

Data properti yang digunakan mayoritas terletak di New York City (sebanyak lebih dari 30.000 data), sedangkan Los Angeles menempati tempat kedua. Apabila lokasi properti digambarkan dalam peta AS, maka dapat digambarkan sebagai berikut :

library(mapdata)
library(ggplot2)
library(rnaturalearth)
library(rnaturalearthdata)
library(rgeos)
library(maps)
library(sf)
library(tools)

world <- ne_countries(scale = "medium", returnclass = "sf")
sites <- data.frame(longitude = airBNB$longitude, 
                    latitude = airBNB$latitude
                    )
states <- st_as_sf(map("state", plot = FALSE, fill = TRUE))
states <- cbind(states, st_coordinates(st_centroid(states)))


ggplot(data = world) +
  geom_sf() +
  geom_sf(data = states, fill = NA) + 
  geom_text(data = states, aes(X, Y, label = ID), size = 2) +
  geom_point(data = sites, aes(x = longitude, y = latitude), size = 3, 
             shape = 16, fill = "red") +
  coord_sf(xlim = c(-121, -70), ylim = c(25, 50), expand = FALSE)+
  labs(title = "Sebaran Properti Airbnb pada Peta AS")

Metode Pemodelan

Metode yang digunakan untuk pemodelan adalah dengan menggunakan regresi linear. Dengan menggunakan software R, pemodelan ini dilakukan dengan memanggil fungsi lm(), dengan fitur price sebagai respon dan variabel lain sebagai prediktor. Nilai alpha yang digunakan untuk menguji hipotesis adalah 0.05.

Fungsi lm() di R akan otomatis mengubah fitur yang bertipe kategorikal menjadi dummy variable

Result

Cluster I : Properti Lama

Tahap pertama dilakukan subsetting terhadap dataset yang memiliki review_scores_rating lebih dari 0 (tidak NA). Data tersebut disimpan dalam variabel bernama subset_airBNB_lama

subset_airBNB_lama<-subset(airBNB,review_scores_rating>=0)

Pemodelan dibuat dengan memanggil fungsi lm() terhadap subset data properti lama

linearModel_airBNB_lama<-lm(log_price ~ property_type+room_type+accommodates+bathrooms+
                  bed_type+cancellation_policy+cleaning_fee+city+instant_bookable+
                  latitude+longitude+review_scores_rating+bedrooms+beds, data=subset_airBNB_lama)
summary(linearModel_airBNB_lama)

Dari hasil tersebut didapatkan nilai akurasi, yang diukur dengan R-squared sebesar 0.627 (62.7%). Adapun distribusi dari nilai residual dapat digambarkan sebagai berikut :

qqnorm(linearModel_airBNB_lama[["residuals"]])
qqline(linearModel_airBNB_lama[["residuals"]])

Dari grafik tersebut terlihat bahwa residual dari pemodelan linear tidak terdistribusi normal. Model kemudian diperbaiki dengan menggunakan feature selection, dengan metode forward & backward (both), dengan sintaks sebagai berikut :

library(mlbench)
library(caret)
library(MASS)

fs_both<-stepAIC(linearModel_airBNB_lama,direction = "both")

Dari proses feature selection, didapatkan hasil bahwa variabel cleaning_fee dapat diabaikan dalam pemodelan. Fitur yang sudah diseleksi kemudian dimodelkan dengan regresi linear sebagai berikut :

linearModel_airBNB_lama_fs<-lm(formula=fs_both[["terms"]],data=subset_airBNB_lama)
summary(linearModel_airBNB_lama_fs)

Namun demikian, akurasi model tetap tidak berubah, yakni berada pada 62.7%. Adapun residual dari model tidak terdistribusi normal.

qqnorm(linearModel_airBNB_lama_fs[["residuals"]])
qqline(linearModel_airBNB_lama_fs[["residuals"]])

Secara teori, apabila variabel independen bernilai 0, maka variabel dependen (log_price) akan bernilai 0. Sehingga, pemodelan dapat dilakukan dengan mengabaikan intercept.

linearModel_airBNB_lama_nointercept<-lm(log_price ~ 0+property_type+room_type+accommodates+bathrooms+
                  bed_type+cancellation_policy+city+instant_bookable+
                  latitude+longitude+review_scores_rating+bedrooms+beds, data=subset_airBNB_lama)
summary(linearModel_airBNB_lama_nointercept)

Hasilnya, terjadi peningkatan akurasi dari 62.7% menjadi 99.2%. Namun demikian, residual tetap tidak terdistribusi linear

qqnorm(linearModel_airBNB_lama_nointercept[["residuals"]])
qqline(linearModel_airBNB_lama_nointercept[["residuals"]])

Cluster 2 : Properti Baru

Pada tahap ini dilakukan pemodelan terhadap properti baru, yakni properti yang belum memiliki rating. Langkah awal adalah dengan membuat subset atas data properti yang belum memiliki rating

subset_airBNB_baru<-subset(airBNB,is.na(review_scores_rating),)

Selanjutnya dilakukan pemodelan dengan menggunakan metode regresi linear lm(), dengan mengabaikan intercept

linearModel_airBNB_baru<-lm(log_price ~ 0+property_type+room_type+accommodates+bathrooms+
                  bed_type+cancellation_policy+cleaning_fee+city+instant_bookable+
                  latitude+longitude+bedrooms+beds, data=subset_airBNB_baru)
summary(linearModel_airBNB_baru)

Dari proses pemodelan diatas, didapatkan akurasi model sebesar 0.985 (98.5%). Bila dilakukan proses feature selection didapatkan hasil sebagai berikut :

fs_both_baru<-stepAIC(linearModel_airBNB_baru,direction = "both")

Dari hasil feature selection, didapatkan hasil bahwa model akan mengabaikan fitur bed_type. Adapun hasil regresi linear yang didapatkan sebagai berikut :

summary(fs_both_baru)

Dari hasil regresi linear dengan fitur yang sudah dilakukan seleksi, akan menghasilkan nilai akurasi yang tidak berubah, yakni sebesar 98.5%

Discussion

Dari hasil pemodelan kedua subset data, yakni data properti Airbnb baru dan existing, didapatkan hasil bahwa fitur berikut mempengaruhi harga sewa properti existing :

Untuk properti baru, fitur yang mempengaruhi, antara lain :

LS0tDQp0aXRsZTogIkVrc3Bsb3Jhc2kgRGF0YSBBaXJibmIiDQphdXRob3I6IA0KLSAgb2xlaCBWaW5jZW50aXVzIEUuIFByYWRoYW5hIChNTVQgLSBJVFMgSmFrYXJ0YSBLZWxhcyBBbmFsaXRpa2EgQmlzbmlzKSAwOTIxMTg1MDA5NjAwMw0Kb3V0cHV0Og0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24NCkFpcmJuYiwgSW5jIChBaXJibmIpIGFkYWxhaCAqbWFya2V0cGxhY2UqIHVudHVrIG1lbWVzYW4gcGVuZ2luYXBhbi8qaG9tZXN0YXkqIGF0YXUgcGFrZXQgbGlidXJhbiwgc2VjYXJhIG9ubGluZSBtZWxhbHVpIGFwbGlrYXNpLiBQZW5nZ3VuYSBjdWt1cCBtZW5ndW5kdWggYXBsaWthc2kgQWlyYm5iIG1lbGFsdWkgR29vZ2xlIFBsYXkvQXBwbGUgU3RvcmUsIGtlbXVkaWFuIHBlbmdndW5hIGFrYW4gbWVuY2FyaSBsb2thc2kgcHJvcGVydGkgcGVuZ2luYXBhbiwgYmVyaWt1dCB0YW5nZ2FsIG1lbmdpbmFwLiBBcGxpa2FzaSBrZW11ZGlhbiBha2FuIG1lbmFtcGlsa2FuIGRhZnRhciBwcm9wZXJ0aSB5YW5nIHRlcnNlZGlhIHBhZGEgbG9rYXNpIGRhbiB0YW5nZ2FsIHRlcnNlYnV0LiBQZW5nZ3VuYSBrZW11ZGlhbiBtZW1iYXlhcmthbiAqZmVlKiBrZXBhZGEgQWlyYm5iLCB1bnR1ayBkaXRlcnVza2FuIGtlcGFkYSBwZW1pbGlrIHByb3BlcnRpLiBEYWxhbSBoYWwgaW5pLCBwZXJ1c2FoYWFuIEFpcmJuYiB0aWRhayBtZW1pbGlraSBhc2V0IHByb3BlcnRpIHlhbmcgZGl0YXdhcmthbiwgbmFtdW4gQWlyYm5iIGJlcnRpbmRhayBzZWJhZ2FpIHBlcmFudGFyYSAoYW50YXJhIHBlbWlsaWsgcHJvcGVydGkgW2hvc3RdIGRhbiBwZW5nZ3VuYSkuIEFpcmJuYiBiZXJrYW50b3IgcHVzYXQgZGkgU2FuIEZyYW5zaXNjbywgQ2FsaWZvcm5pYSwgQW1lcmlrYSBTZXJpa2F0Lg0KDQpEYXJpIHByb3BlcnRpIHlhbmcgdGVyc2VkaWEgZGkgQWlyYm5iLCBhcGFrYWggZmFrdG9yIHBlbmVudHUgaGFyZ2EgcHJvcGVydGkgZGkgQWlyYm5iPw0KDQojIE1ldGhvZG9sb2d5DQoNCiMjIERhdGEgVW5kZXJzdGFuZGluZw0KDQpTdGV2ZVpoZW5nIChTdGV2ZSBaaGVuZyksIG1lcnVwYWthbiBrb250cmlidXRvciBkaSBLYWdnbGUuY29tIHlhbmcgbWVuZ3VuZ2dhaCBkYXRhc2V0IFsiQWlyYm5iIHByaWNlIHByZWRpY3Rpb24iXShodHRwczovL3d3dy5rYWdnbGUuY29tL3N0ZXZlemhlbmdocC9haXJibmItcHJpY2UtcHJlZGljdGlvbikuIERhdGFzZXQgbWVydXBha2FuIGRhdGEgcHJvcGVydGkgQWlyYm5iIHlhbmcgYWRhIGRpIEFtZXJpa2EgU2VyaWthdC4gRGF0YXNldCB0ZXJkaXJpIGRhcmkgKio3NDExMSoqIGJhcmlzIGRhdGEsIGRlbmdhbiAqKjI5KiogZml0dXINCg0KRGVza3JpcHNpIGZpdHVyLWZpdHVyIHlhbmcgdGVyc2VkaWEgKG1lbnVydXQgaW50ZXJwcmV0YXNpIHBlbnVsaXMsIGRpa2FyZW5ha2FuIHRpZGFrIGFkYW55YSBkb2t1bWVudGFzaSByZXNtaSBkYXJpIGtvbnRyaWJ1dG9yKSBkYW4geWFuZyBkaWd1bmFrYW4sIGFkYWxhaCBzZWJhZ2FpIGJlcmlrdXQgOiANCg0KfE5vfCBGZWF0dXJlIHwgRGVzY3JpcHRpb24gfCBGaXR1ciBkaWd1bmFrYW4/IHwgUmVzcG9uc2UvUHJlZGljdG9yIHwNCnwgLS0tLS0tLS0tLS0gfC0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tIHwNCnwxfCBpZCB8IE5vbW9yIElEIHByb3BlcnRpIHwgVGlkYWsgfCAtIHwNCnwyfCBsb2dfcHJpY2UgfCBIYXJnYSBzZXdhIHByb3BlcnRpIChkYWxhbSBiZW50dWsgbG9nKSB8IFlhIHwgUmVzcG9uc2UgfA0KfDN8IHByb3BlcnR5X3R5cGUgfCBKZW5pcyBwcm9wZXJ0aSAoY29udG9oIDogYXBhcnRlbWVuLCBydW1haCwgZHNiKSB8IFlhIHwgUHJlZGljdG9yfA0KfDR8IHJvb21fdHlwZSB8IEplbmlzIGthbWFyIHlhbmcgZGlzZXdha2FuLiBDb250b2ggOiAxKSBlbnRpcmUgaG9tZS9hcHQgLS0+IHNlbHVydWggcnVtYWgvYXBhcnRlbWVuLCAyKSBwcml2YXRlIHJvb20gLS0+IGthbWFyIHByaWJhZGkgeWFuZyBkaXNld2FrYW4sIDMpIFNoYXJlZCByb29tIC0tPiBrYW1hciB5YW5nIGRpYmFnaSBkZW5nYW4gcGVuZ2d1bmEgbGFpbikgfFlhfFByZWRpY3RvcnwNCnw1fGFtZW5pdGllc3xLZWxlbmdrYXBhbiBsYWluIGRhcmkgcHJvcGVydGl8VGlkYWt8LXwNCnw2fGFjY29tb2RhdGVzfEp1bWxhaCBrYXBhc2l0YXMgb3JhbmcgeWFuZyBkYXBhdCBtZW5naW5hcCBkYWxhbSBwcm9wZXJ0aSB5YW5nIGRpc2V3YWthbnxZYXxQcmVkaWN0b3J8DQp8N3xiYXRocm9vbXN8SnVtbGFoIGthbWFyIG1hbmRpIHRlcnNlZGlhfFlhfFByZWRpY3RvcnwNCnw4fGJlZF90eXBlfEplbmlzIGthc3VyIHlhbmcgZGlzZWRpYWthbiA6IFJlYWwgQmVkLCBGdXRvbiwgUHVsbC1vdXQgU29mYSwgQ291Y2ggYXRhdSBBaXJiZWR8WWF8UHJlZGljdG9yfA0KfDl8Y2FuY2VsbGF0aW9uX3BvbGljeXxQZXJzeWFyYXRhbiBwZW1iYXRhbGFufFlhfFByZWRpY3RvcnwNCnwxMHxjbGVhbmluZ19mZWV8QWRhIGF0YXUgdGlkYWtueWEgYmlheWEgcGVtYmVyc2loYW4ga2FtYXJ8WWF8UHJlZGljdG9yfA0KfDExfGNpdHl8S290YSBsZXRhayBwcm9wZXJ0aSBiZXJhZGF8WWF8UHJlZGljdG9yfA0KfDEyfGRlc2NyaXB0aW9ufERlc2tyaXBzaSBwcm9wZXJ0aXxUaWRha3wtfA0KfDEzfGZpcnN0X3Jldmlld3xUYW5nZ2FsIHJldmlldyBwZXJ0YW1hfFlhfFByZWRpY3RvcnwNCnwxNHxob3N0X2hhc19wcm9maWxlX3BpY3xQZW5hbmRhIGFwYWthaCAqaG9zdCogbWVuYW1waWxrYW4gZm90byBwcm9maWwgcGFkYSBha3VuIEFpcmJuYnxUaWRha3wtfA0KfDE1fGhvc3RfaWRlbnRpdHlfdmVyaWZpZWR8UGVuYW5kYSBhcGFrYWggKmhvc3QqIHRlbGFoIGRpdmVyaWZpa2FzaSBvbGVoIEFpcmJuYnxUaWRha3wtfA0KfDE2fGhvc3RfcmVzcG9uc2VfcmF0ZXxLZWNlcGF0YW4gKmhvc3QqIGRhbGFtIG1lcmVzcG9ucyBzZXRpYXAgcGVydGFueWFhbiB5YW5nIG1hc3VrfFRpZGFrfC18DQp8MTd8aG9zdF9zaW5jZXxUYW5nZ2FsICpob3N0KiBtdWxhaSBtZW1hc3Vra2FuIHByb3BlcnRpbnlhIHBhZGEgQWlyYm5ifFRpZGFrfC18DQp8MTh8aW5zdGFudF9ib29rYWJsZXxQZW5hbmRhIGFwYWthaCBwcm9wZXJ0aSBkYXBhdCBsYW5nc3VuZyBkaSBib29rIHRhbnBhIHBlcmx1IGFkYW55YSBrb25maXJtYXNpIGRhcmkgKmhvc3QqfFlhfFByZWRpY3RvcnwNCnwxOXxsYXN0X3Jldmlld3xUYW5nZ2FsIHJldmlldyB0ZXJha2hpcnxUaWRha3wtfA0KfDIwfGxhdGl0dWRlfCpMYXRpdHVkZSogbG9rYXNpIHByb3BlcnRpfFlhfFByZWRpY3RvcnwNCnwyMXxsb25naXR1ZGV8KkxvbmdpdHVkZSogbG9rYXNpIHByb3BlcnRpfFlhfFByZWRpY3RvcnwNCnwyMnxuYW1lfE5hbWEgcHJvcGVydGl8VGlkYWt8LXwNCnwyM3xuZWlnaGJvdXJob29kfG5hbWEgd2lsYXlhaHxUaWRha3wtfA0KfDI0fG51bWJlcl9vZl9yZXZpZXdzfGp1bWxhaCByZXZpZXcgeWFuZyBkaWJlcmlrYW58VGlkYWt8LXwNCnwyNXxyZXZpZXdfc2NvcmVzX3JhdGluZ3xTa29yIHJldmlld3xZYXxQcmVkaWN0b3J8DQp8MjZ8dGh1bWJuYWlsX3VybHxVUkwgbGluayBnYW1iYXIgZGFyaSBwcm9wZXJ0aXxUaWRha3wtfA0KfDI3fHppcGNvZGV8S29kZXBvcyBwcm9wZXJ0aXxUaWRha3wtfA0KfDI4fGJlZHJvb21zfEJhbnlhayBrYW1hciB0aWR1cnxZYXxQcmVkaWN0b3J8DQp8Mjl8YmVkc3xCYW55YWsgdGVtcGF0IHRpZHVyIHlhbmcgZGlzZWRpYWthbnxZYXxQcmVkaWN0b3J8DQoNCkFkYXB1biByaW5na2FzYW4gZGFyaSBuaWxhaSBwYWRhIHNldGlhcCBmZWF0dXJlIGFkYWxhaCBzZWJhZ2FpIGJlcmlrdXQgOg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYWlyQk5CPC1yZWFkX2NzdigidHJhaW4uY3N2IikNCnN1bW1hcnkoYWlyQk5CKQ0KYGBgDQoNCkRhcmkgc3VtbWFyeSBkaWF0YXMsIGRpa2V0YWh1aSBiYWh3YSB0ZXJkYXBhdCBiZWJlcmFwYSBmZWF0dXJlIHlhbmcgbWVtaWxpa2kgbmlsYWkgTkEuIEZpdHVyIHJldmlld19zY29yZXNfcmF0aW5nIG1lbWlsaWtpIE5BIHlhbmcgY3VrdXAgdGluZ2dpLiBIYWwgaW5pIGRpYWtpYmF0a2FuIGthcmVuYSBwcm9wZXJ0aSB0ZXJzZWJ1dCBiZWx1bSBtZW1pbGlraSByYXRpbmcuIERpZHVnYSBiYWh3YSBwcm9wZXJ0aSB0ZXJzZWJ1dCBhZGFsYWggcHJvcGVydGkgYmFydS4gT2xlaCBrYXJlbmEgaXR1LCBkYXRhc2V0IGFrYW4gZGliYWdpIG1lbmphZGkgMiAqY2x1c3RlciosIHlha25pIGRhdGFzZXQgdW50dWsgcHJvcGVydGkgYmFydSAoYmVsdW0gcGVybmFoIGRpcGVzYW4pIGRhbiBwcm9wZXJ0aSBsYW1hIChwZXJuYWggZGlwZXNhbikuIEFkYXB1biAqaW5zdGFuY2UqIHlhbmcgbWVtaWxpa2kgbmlsYWkgTkEgdW50dWsgc2V0aWFwIGNsdXN0ZXIgbnlhIGFrYW4gZGlhYmFpa2FuIGRhbiB0aWRhayBkaWd1bmFrYW4gdW50dWsgcHJvc2VzIHBlbW9kZWxhbi4NCg0KRGFyaSBkYXRhIHRlcmRhcGF0ICppbnN0YW5jZSogeWFuZyBuaWxhaSBsb2dfcHJpY2UgbnlhIGFkYWxhaCAwLiBUZXJrYWl0IGhhbCBpbmksIGluc3RhbmNlIHRlcnNlYnV0IHRpZGFrIGFrYW4gZGltYXN1a2thbiBrZWRhbGFtIHByb3NlcyBtb2RlbGxpbmcNCmBgYHtyfQ0KYWlyQk5CPC1zdWJzZXQoYWlyQk5CLGxvZ19wcmljZT4wKQ0KYGBgDQoNCktvcmVsYXNpIGRhdGEgbnVtZXJpayBkYXBhdCBkaXNhamlrYW4gc2ViYWdhaSBiZXJpa3V0IDogDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjb3JycGxvdCkNCnN1YnNldF9haXJCTkJfbnVtZXJpYzwtYXMubWF0cml4KG5hLm9taXQoYWlyQk5CICU+JSBzZWxlY3QoMiw2LDcsMjQsMjUpKSkNCmNvcnJwbG90KGNvcihzdWJzZXRfYWlyQk5CX251bWVyaWMpLCBtZXRob2Q9ImNvbG9yIixpcy5jb3JyID0gVFJVRSkNCmBgYA0KDQpEYXJpIGdyYWZpayBkaWF0YXMsIGRhcGF0IHRlcmxpaGF0IGJhaHdhIGBsb2dfcHJpY2VgIGJlcmtvcmVsYXNpIGt1YXQgZGVuZ2FuIGZpdHVyIGBhY2NvbW9kYXRlc2AgZGFuIGBiYXRocm9vbXNgLiBTZW1ha2luIGJhbnlhayBwZW5naW5hcCB5YW5nIGRhcGF0IGRpYWtvbW9kaXIgZGFuIHNlbWFraW4gYmFueWFrIGp1bWxhaCBrYW1hciBtYW5kaSwgbWFrYSBzZW1ha2luIHRpbmdnaSBoYXJnYSBzZXdhIHByb3BlcnRpIHRlcnNlYnV0LiBEYWxhbSBoYWwgaW5pLCB0aWRhayBhZGEgcGVuZ2FydWgganVtbGFoIHJldmlldyB0ZXJoYWRhcCByYXRpbmcgc2NvcmUuDQoNClNlY2FyYSBnYXJpcyBiZXNhciwgZGF0YSB5YW5nIGRpZ3VuYWthbiBtZW1pbGlraSBzZWJhcmFuIGhhcmdhIHNlYmFnYWkgYmVyaWt1dCA6IA0KYGBge3J9DQpnZ3Bsb3QoYWlyQk5CLGFlcyhsb2dfcHJpY2UpKStnZW9tX2hpc3RvZ3JhbShiaW5zPTIwKStsYWJzKHRpdGxlID0gIkRpc3RyaWJ1c2kgSGFyZ2EgUHJvcGVydGkiLCB4PSJIYXJnYSAoZGFsYW0gZnVuZ3NpIGxvZykiLCB5PSJKdW1sYWggUHJvcGVydGkiKQ0KDQpnZ3Bsb3QoYWlyQk5CLGFlcyh4PU5VTEwsIHk9bG9nX3ByaWNlKSkrZ2VvbV9ib3hwbG90KCkrbGFicyh0aXRsZSA9ICJEaXN0cmlidXNpIEhhcmdhIFByb3BlcnRpIiwgeT0iSGFyZ2EgKGRhbGFtIGZ1bmdzaSBsb2cpIix4PU5VTEwpDQpgYGANCg0KRGFyaSBnYW1iYXIgdGVybGloYXQgYmFod2EgaGFyZ2EgbWF5b3JpdGFzIGJlcmFkYSBwYWRhIHJlbnRhbmcgNC4zIC0gNS4yDQoNCkp1bWxhaCBwcm9wZXJ0aSBiZXJkYXNhcmthbiBqZW5pcyBueWEsIGRhcGF0IGRpdGFtcGlsa2FuIGRhbGFtIGdyYWZpayBzZWJhZ2FpIGJlcmlrdXQgOiANCmBgYHtyfQ0KZ2dwbG90KGFpckJOQixhZXMocHJvcGVydHlfdHlwZSkpK2dlb21fYmFyKCkrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpK2xhYnModGl0bGUgPSAiSnVtbGFoIFByb3BlcnRpIGJlcmRhc2Fya2FuIEplbmlzIFByb3BlcnRpIiwgeT0iSmVuaXMgUHJvcGVydGkiLHg9Ikp1bWxhaCBQcm9wZXJ0aSIpKyBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKHVuaXF1ZShhaXJCTkIkcHJvcGVydHlfdHlwZSkpKQ0KYGBgDQoNClBhZGEgZ3JhZmlrIGRpYXRhcywgdGVybGloYXQgYmFod2EgZGF0YXNldCBkaWRvbWluYXNpIGRlbmdhbiBwcm9wZXJ0aSBiZXJqZW5pcyBBcGFydGVtZW50IChhcGFydGVtZW4pIGRhbiBIb3VzZSAocnVtYWgpLiBEaXN0cmlidXNpIGp1bWxhaCBwcm9wZXJ0aSBiZXJkYXNhcmthbiBqdW1sYWggcGVuZ2luYXAgZGFwYXQgZGlnYW1iYXJrYW4gc2ViYWdhaSBiZXJpa3V0IDogDQoNCmBgYHtyfQ0KZ2dwbG90KGFpckJOQixhZXMoYWNjb21tb2RhdGVzKSkrZ2VvbV9iYXIoKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkrbGFicyh0aXRsZSA9ICJEaXN0cmlidXNpIEp1bWxhaCBQcm9wZXJ0aSBiZXJkYXNhcmthbiBKdW1sYWggUGVuZ2luYXAgeWFuZyBiaXNhIERpdGFtcHVuZyIsIHk9Ikp1bWxhaCBQcm9wZXJ0aSIseD0iSnVtbGFoIFBlbmdpbmFwIikgKyBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDE6MToxNikpDQpgYGANCg0KRGFyaSBncmFmaWsgdGVyc2VidXQsIG1heW9yaXRhcyBwcm9wZXJ0aSBkYXBhdCBtZW5hbXB1bmcgbWFrc2ltYWwgMiBwZW5naW5hcC4gVGVyZGFwYXQgYmViZXJhcGEgcHJvcGVydGkgeWFuZyBkYXBhdCBtZW5hbXB1bmcgc2FtcGFpIDE2IHBlbmdpbmFwLiANCg0KQmVyZGFzYXJrYW4ganVtbGFoIGthbWFyIG1hbmRpIHlhbmcgdGVyc2VkaWEgcGFkYSBzZXRpYXAgcHJvcGVydGksIGRhcGF0IGRpZ2FtYmFya2FuIGRhbGFtIGdyYWZpayBkaXN0cmlidXNpIHNlYmFnYWkgYmVyaWt1dCA6DQoNCmBgYHtyfQ0KZ2dwbG90KGFpckJOQixhZXMoYmF0aHJvb21zKSkrZ2VvbV9iYXIoKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkrbGFicyh0aXRsZSA9ICJEaXN0cmlidXNpIEp1bWxhaCBQcm9wZXJ0aSBiZXJkYXNhcmthbiBKdW1sYWggS2FtYXIgTWFuZGkiLCB5PSJKdW1sYWggUHJvcGVydGkiLHg9Ikp1bWxhaCBLYW1hciBNYW5kaSIpICsgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9c2VxKGZyb20gPSAwLCB0byA9IDgsIGJ5ID0gMC41KSkNCmBgYA0KDQpEYXJpIGdyYWZpayBkYXBhdCBkaXR1bmp1a2thbiBiYWh3YSBtaW5pbWFsIHNlYnVhaCBwcm9wZXJ0aSBtZW1pbGlraSAxIGthbWFyIG1hbmRpLiBCZWJlcmFwYSBwcm9wZXJ0aSBtZW1pbGlraSBrZWxpcGF0YW4gMC41LCBpbmkgYXJ0aW55YSBiYWh3YSBwcm9wZXJ0aSB0ZXJzZWJ1dCBoYW55YSBtZW1pbGlraSBrbG9zZXQgZGFuIHdhc3RhZmVsLCBuYW11biB0aWRhayBtZW1pbGlraSAqYmF0aCB0dWIqIFsid2hhdCBkb2VzIDEgMS8yIGJhdGggbWVhbnM/Il0oaHR0cHM6Ly93d3cuaG9tZXMuY29tL3F1ZXN0aW9uLzE1ODQ4L3doYXQtZG9lcy0xLTEtMi1iYXRoLW1lYW5zLykuDQoNCkJlcmRhc2Fya2FuIGtvdGEsIGRpc3RyaWJ1c2kgcHJvcGVydGkgZGFwYXQgZGlnYW1iYXJrYW4gc2ViYWdhaSBiZXJpa3V0IDogDQoNCmBgYHtyfQ0KZ2dwbG90KGFpckJOQixhZXMoY2l0eSkpK2dlb21fYmFyKCkrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpK2xhYnModGl0bGUgPSAiRGlzdHJpYnVzaSBKdW1sYWggUHJvcGVydGkgYmVyZGFzYXJrYW4gS290YSIsIHk9Ikp1bWxhaCBQcm9wZXJ0aSIseD0iS290YSIpDQpgYGANCg0KRGF0YSBwcm9wZXJ0aSB5YW5nIGRpZ3VuYWthbiBtYXlvcml0YXMgdGVybGV0YWsgZGkgTmV3IFlvcmsgQ2l0eSAoc2ViYW55YWsgbGViaWggZGFyaSAzMC4wMDAgZGF0YSksIHNlZGFuZ2thbiBMb3MgQW5nZWxlcyBtZW5lbXBhdGkgdGVtcGF0IGtlZHVhLiBBcGFiaWxhIGxva2FzaSBwcm9wZXJ0aSBkaWdhbWJhcmthbiBkYWxhbSBwZXRhIEFTLCBtYWthIGRhcGF0IGRpZ2FtYmFya2FuIHNlYmFnYWkgYmVyaWt1dCA6IA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShtYXBkYXRhKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShybmF0dXJhbGVhcnRoKQ0KbGlicmFyeShybmF0dXJhbGVhcnRoZGF0YSkNCmxpYnJhcnkocmdlb3MpDQpsaWJyYXJ5KG1hcHMpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0b29scykNCg0Kd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikNCnNpdGVzIDwtIGRhdGEuZnJhbWUobG9uZ2l0dWRlID0gYWlyQk5CJGxvbmdpdHVkZSwgDQogICAgICAgICAgICAgICAgICAgIGxhdGl0dWRlID0gYWlyQk5CJGxhdGl0dWRlDQogICAgICAgICAgICAgICAgICAgICkNCnN0YXRlcyA8LSBzdF9hc19zZihtYXAoInN0YXRlIiwgcGxvdCA9IEZBTFNFLCBmaWxsID0gVFJVRSkpDQpzdGF0ZXMgPC0gY2JpbmQoc3RhdGVzLCBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChzdGF0ZXMpKSkNCg0KDQpnZ3Bsb3QoZGF0YSA9IHdvcmxkKSArDQogIGdlb21fc2YoKSArDQogIGdlb21fc2YoZGF0YSA9IHN0YXRlcywgZmlsbCA9IE5BKSArIA0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRlcywgYWVzKFgsIFksIGxhYmVsID0gSUQpLCBzaXplID0gMikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBzaXRlcywgYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSksIHNpemUgPSAzLCANCiAgICAgICAgICAgICBzaGFwZSA9IDE2LCBmaWxsID0gInJlZCIpICsNCiAgY29vcmRfc2YoeGxpbSA9IGMoLTEyMSwgLTcwKSwgeWxpbSA9IGMoMjUsIDUwKSwgZXhwYW5kID0gRkFMU0UpKw0KICBsYWJzKHRpdGxlID0gIlNlYmFyYW4gUHJvcGVydGkgQWlyYm5iIHBhZGEgUGV0YSBBUyIpDQoNCg0KYGBgDQoNCg0KIyMgTWV0b2RlIFBlbW9kZWxhbg0KTWV0b2RlIHlhbmcgZGlndW5ha2FuIHVudHVrIHBlbW9kZWxhbiBhZGFsYWggZGVuZ2FuIG1lbmdndW5ha2FuIHJlZ3Jlc2kgbGluZWFyLiBEZW5nYW4gbWVuZ2d1bmFrYW4gc29mdHdhcmUgUiwgcGVtb2RlbGFuIGluaSBkaWxha3VrYW4gZGVuZ2FuIG1lbWFuZ2dpbCBmdW5nc2kgYGxtKClgLCBkZW5nYW4gZml0dXIgYHByaWNlYCBzZWJhZ2FpIHJlc3BvbiBkYW4gdmFyaWFiZWwgbGFpbiBzZWJhZ2FpIHByZWRpa3Rvci4gTmlsYWkgYWxwaGEgeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuZ3VqaSBoaXBvdGVzaXMgYWRhbGFoIDAuMDUuDQoNCkZ1bmdzaSBgbG0oKWAgZGkgUiBha2FuIG90b21hdGlzIG1lbmd1YmFoIGZpdHVyIHlhbmcgYmVydGlwZSBrYXRlZ29yaWthbCBtZW5qYWRpICpkdW1teSB2YXJpYWJsZSoNCg0KIyBSZXN1bHQNCg0KIyMgQ2x1c3RlciBJIDogUHJvcGVydGkgTGFtYQ0KVGFoYXAgcGVydGFtYSBkaWxha3VrYW4gKnN1YnNldHRpbmcqIHRlcmhhZGFwIGRhdGFzZXQgeWFuZyBtZW1pbGlraSBgcmV2aWV3X3Njb3Jlc19yYXRpbmdgIGxlYmloIGRhcmkgMCAodGlkYWsgTkEpLiBEYXRhIHRlcnNlYnV0IGRpc2ltcGFuIGRhbGFtIHZhcmlhYmVsIGJlcm5hbWEgYHN1YnNldF9haXJCTkJfbGFtYWANCmBgYHtyfQ0Kc3Vic2V0X2FpckJOQl9sYW1hPC1zdWJzZXQoYWlyQk5CLHJldmlld19zY29yZXNfcmF0aW5nPj0wKQ0KYGBgDQoNClBlbW9kZWxhbiBkaWJ1YXQgZGVuZ2FuIG1lbWFuZ2dpbCBmdW5nc2kgYGxtKClgIHRlcmhhZGFwIHN1YnNldCBkYXRhIHByb3BlcnRpIGxhbWENCmBgYHtyfQ0KbGluZWFyTW9kZWxfYWlyQk5CX2xhbWE8LWxtKGxvZ19wcmljZSB+IHByb3BlcnR5X3R5cGUrcm9vbV90eXBlK2FjY29tbW9kYXRlcytiYXRocm9vbXMrDQogICAgICAgICAgICAgICAgICBiZWRfdHlwZStjYW5jZWxsYXRpb25fcG9saWN5K2NsZWFuaW5nX2ZlZStjaXR5K2luc3RhbnRfYm9va2FibGUrDQogICAgICAgICAgICAgICAgICBsYXRpdHVkZStsb25naXR1ZGUrcmV2aWV3X3Njb3Jlc19yYXRpbmcrYmVkcm9vbXMrYmVkcywgZGF0YT1zdWJzZXRfYWlyQk5CX2xhbWEpDQpzdW1tYXJ5KGxpbmVhck1vZGVsX2FpckJOQl9sYW1hKQ0KYGBgDQoNCkRhcmkgaGFzaWwgdGVyc2VidXQgZGlkYXBhdGthbiBuaWxhaSBha3VyYXNpLCB5YW5nIGRpdWt1ciBkZW5nYW4gUi1zcXVhcmVkIHNlYmVzYXIgMC42MjcgKDYyLjclKS4gQWRhcHVuIGRpc3RyaWJ1c2kgZGFyaSBuaWxhaSByZXNpZHVhbCBkYXBhdCBkaWdhbWJhcmthbiBzZWJhZ2FpIGJlcmlrdXQgOiANCg0KYGBge3J9DQpxcW5vcm0obGluZWFyTW9kZWxfYWlyQk5CX2xhbWFbWyJyZXNpZHVhbHMiXV0pDQpxcWxpbmUobGluZWFyTW9kZWxfYWlyQk5CX2xhbWFbWyJyZXNpZHVhbHMiXV0pDQoNCmBgYA0KRGFyaSBncmFmaWsgdGVyc2VidXQgdGVybGloYXQgYmFod2EgcmVzaWR1YWwgZGFyaSBwZW1vZGVsYW4gbGluZWFyIHRpZGFrIHRlcmRpc3RyaWJ1c2kgbm9ybWFsLiBNb2RlbCBrZW11ZGlhbiBkaXBlcmJhaWtpIGRlbmdhbiBtZW5nZ3VuYWthbiAqZmVhdHVyZSBzZWxlY3Rpb24qLCBkZW5nYW4gbWV0b2RlIGZvcndhcmQgJiBiYWNrd2FyZCAoYm90aCksIGRlbmdhbiBzaW50YWtzIHNlYmFnYWkgYmVyaWt1dCA6IA0KDQpgYGB7cn0NCmxpYnJhcnkobWxiZW5jaCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KE1BU1MpDQoNCmZzX2JvdGg8LXN0ZXBBSUMobGluZWFyTW9kZWxfYWlyQk5CX2xhbWEsZGlyZWN0aW9uID0gImJvdGgiKQ0KYGBgDQoNCkRhcmkgcHJvc2VzIGZlYXR1cmUgc2VsZWN0aW9uLCBkaWRhcGF0a2FuIGhhc2lsIGJhaHdhIHZhcmlhYmVsIGBjbGVhbmluZ19mZWVgIGRhcGF0IGRpYWJhaWthbiBkYWxhbSBwZW1vZGVsYW4uIEZpdHVyIHlhbmcgc3VkYWggZGlzZWxla3NpIGtlbXVkaWFuIGRpbW9kZWxrYW4gZGVuZ2FuIHJlZ3Jlc2kgbGluZWFyIHNlYmFnYWkgYmVyaWt1dCA6IA0KYGBge3J9DQpsaW5lYXJNb2RlbF9haXJCTkJfbGFtYV9mczwtbG0oZm9ybXVsYT1mc19ib3RoW1sidGVybXMiXV0sZGF0YT1zdWJzZXRfYWlyQk5CX2xhbWEpDQpzdW1tYXJ5KGxpbmVhck1vZGVsX2FpckJOQl9sYW1hX2ZzKQ0KYGBgDQoNCk5hbXVuIGRlbWlraWFuLCBha3VyYXNpIG1vZGVsIHRldGFwIHRpZGFrIGJlcnViYWgsIHlha25pIGJlcmFkYSBwYWRhIDYyLjclLiBBZGFwdW4gcmVzaWR1YWwgZGFyaSBtb2RlbCB0aWRhayB0ZXJkaXN0cmlidXNpIG5vcm1hbC4NCmBgYHtyfQ0KcXFub3JtKGxpbmVhck1vZGVsX2FpckJOQl9sYW1hX2ZzW1sicmVzaWR1YWxzIl1dKQ0KcXFsaW5lKGxpbmVhck1vZGVsX2FpckJOQl9sYW1hX2ZzW1sicmVzaWR1YWxzIl1dKQ0KYGBgDQoNClNlY2FyYSB0ZW9yaSwgYXBhYmlsYSB2YXJpYWJlbCBpbmRlcGVuZGVuIGJlcm5pbGFpIDAsIG1ha2EgdmFyaWFiZWwgZGVwZW5kZW4gKGxvZ19wcmljZSkgYWthbiBiZXJuaWxhaSAwLiBTZWhpbmdnYSwgcGVtb2RlbGFuIGRhcGF0IGRpbGFrdWthbiBkZW5nYW4gbWVuZ2FiYWlrYW4gKmludGVyY2VwdCouDQpgYGB7cn0NCmxpbmVhck1vZGVsX2FpckJOQl9sYW1hX25vaW50ZXJjZXB0PC1sbShsb2dfcHJpY2UgfiAwK3Byb3BlcnR5X3R5cGUrcm9vbV90eXBlK2FjY29tbW9kYXRlcytiYXRocm9vbXMrDQogICAgICAgICAgICAgICAgICBiZWRfdHlwZStjYW5jZWxsYXRpb25fcG9saWN5K2NpdHkraW5zdGFudF9ib29rYWJsZSsNCiAgICAgICAgICAgICAgICAgIGxhdGl0dWRlK2xvbmdpdHVkZStyZXZpZXdfc2NvcmVzX3JhdGluZytiZWRyb29tcytiZWRzLCBkYXRhPXN1YnNldF9haXJCTkJfbGFtYSkNCnN1bW1hcnkobGluZWFyTW9kZWxfYWlyQk5CX2xhbWFfbm9pbnRlcmNlcHQpDQpgYGANCg0KSGFzaWxueWEsIHRlcmphZGkgcGVuaW5na2F0YW4gYWt1cmFzaSBkYXJpIDYyLjclIG1lbmphZGkgOTkuMiUuIE5hbXVuIGRlbWlraWFuLCByZXNpZHVhbCB0ZXRhcCB0aWRhayB0ZXJkaXN0cmlidXNpIGxpbmVhcg0KDQpgYGB7cn0NCnFxbm9ybShsaW5lYXJNb2RlbF9haXJCTkJfbGFtYV9ub2ludGVyY2VwdFtbInJlc2lkdWFscyJdXSkNCnFxbGluZShsaW5lYXJNb2RlbF9haXJCTkJfbGFtYV9ub2ludGVyY2VwdFtbInJlc2lkdWFscyJdXSkNCmBgYA0KDQoNCiMjIENsdXN0ZXIgMiA6IFByb3BlcnRpIEJhcnUNCg0KUGFkYSB0YWhhcCBpbmkgZGlsYWt1a2FuIHBlbW9kZWxhbiB0ZXJoYWRhcCBwcm9wZXJ0aSBiYXJ1LCB5YWtuaSBwcm9wZXJ0aSB5YW5nIGJlbHVtIG1lbWlsaWtpIHJhdGluZy4gTGFuZ2thaCBhd2FsIGFkYWxhaCBkZW5nYW4gbWVtYnVhdCAqc3Vic2V0KiBhdGFzIGRhdGEgcHJvcGVydGkgeWFuZyBiZWx1bSBtZW1pbGlraSByYXRpbmcNCg0KYGBge3J9DQpzdWJzZXRfYWlyQk5CX2JhcnU8LXN1YnNldChhaXJCTkIsaXMubmEocmV2aWV3X3Njb3Jlc19yYXRpbmcpLCkNCmBgYA0KDQpTZWxhbmp1dG55YSBkaWxha3VrYW4gcGVtb2RlbGFuIGRlbmdhbiBtZW5nZ3VuYWthbiBtZXRvZGUgcmVncmVzaSBsaW5lYXIgYGxtKClgLCBkZW5nYW4gbWVuZ2FiYWlrYW4gKmludGVyY2VwdCoNCg0KYGBge3J9DQpsaW5lYXJNb2RlbF9haXJCTkJfYmFydTwtbG0obG9nX3ByaWNlIH4gMCtwcm9wZXJ0eV90eXBlK3Jvb21fdHlwZSthY2NvbW1vZGF0ZXMrYmF0aHJvb21zKw0KICAgICAgICAgICAgICAgICAgYmVkX3R5cGUrY2FuY2VsbGF0aW9uX3BvbGljeStjbGVhbmluZ19mZWUrY2l0eStpbnN0YW50X2Jvb2thYmxlKw0KICAgICAgICAgICAgICAgICAgbGF0aXR1ZGUrbG9uZ2l0dWRlK2JlZHJvb21zK2JlZHMsIGRhdGE9c3Vic2V0X2FpckJOQl9iYXJ1KQ0Kc3VtbWFyeShsaW5lYXJNb2RlbF9haXJCTkJfYmFydSkNCmBgYA0KDQpEYXJpIHByb3NlcyBwZW1vZGVsYW4gZGlhdGFzLCBkaWRhcGF0a2FuIGFrdXJhc2kgbW9kZWwgc2ViZXNhciAwLjk4NSAoOTguNSUpLiBCaWxhIGRpbGFrdWthbiBwcm9zZXMgKmZlYXR1cmUgc2VsZWN0aW9uKiBkaWRhcGF0a2FuIGhhc2lsIHNlYmFnYWkgYmVyaWt1dCA6DQoNCmBgYHtyfQ0KZnNfYm90aF9iYXJ1PC1zdGVwQUlDKGxpbmVhck1vZGVsX2FpckJOQl9iYXJ1LGRpcmVjdGlvbiA9ICJib3RoIikNCmBgYA0KRGFyaSBoYXNpbCAqZmVhdHVyZSBzZWxlY3Rpb24qLCBkaWRhcGF0a2FuIGhhc2lsIGJhaHdhIG1vZGVsIGFrYW4gbWVuZ2FiYWlrYW4gZml0dXIgYGJlZF90eXBlYC4gQWRhcHVuIGhhc2lsIHJlZ3Jlc2kgbGluZWFyIHlhbmcgZGlkYXBhdGthbiBzZWJhZ2FpIGJlcmlrdXQgOiANCg0KYGBge3J9DQpzdW1tYXJ5KGZzX2JvdGhfYmFydSkNCmBgYA0KDQpEYXJpIGhhc2lsIHJlZ3Jlc2kgbGluZWFyIGRlbmdhbiBmaXR1ciB5YW5nIHN1ZGFoIGRpbGFrdWthbiBzZWxla3NpLCBha2FuIG1lbmdoYXNpbGthbiBuaWxhaSBha3VyYXNpIHlhbmcgdGlkYWsgYmVydWJhaCwgeWFrbmkgc2ViZXNhciA5OC41JQ0KDQojIERpc2N1c3Npb24NCg0KRGFyaSBoYXNpbCBwZW1vZGVsYW4ga2VkdWEgc3Vic2V0IGRhdGEsIHlha25pIGRhdGEgcHJvcGVydGkgQWlyYm5iIGJhcnUgZGFuICpleGlzdGluZyosIGRpZGFwYXRrYW4gaGFzaWwgYmFod2EgZml0dXIgYmVyaWt1dCBtZW1wZW5nYXJ1aGkgaGFyZ2Egc2V3YSBwcm9wZXJ0aSAqZXhpc3RpbmcqIDogDQoNCi0gVGlwZSBwcm9wZXJ0aSAoY29udG9oIDogVmlsbGEsIFJ1bWFoLCBLb25kb21pbml1bSwgZHNiKQ0KLSBUaXBlIGthbWFyIChjb250b2ggOiBrYW1hciBwcmliYWRpLCBzaGFyZWQgcm9vbSwgZHNiKQ0KLSBKdW1sYWggdGFtdSB5YW5nIGRhcGF0IGRpdGFtcHVuZyBkYWxhbSBwcm9wZXJ0aS4gU2VtYWtpbiBiYW55YWsganVtbGFoIHRhbXUgeWFuZyBkYXBhdCBkaSB0YW1wdW5nLCBzZW1ha2luIG1haGFsIGhhcmdhIHNld2FueWENCi0gSnVtbGFoIGthbWFyIG1hbmRpIHlhbmcgdGVyc2VkaWENCi0gSmVuaXMgKmJlZCogeWFuZyBkaXNlZGlha2FuLiBQcm9wZXJ0aSB5YW5nIG1lbWlsaWtpIGJlZCBiZXJqZW5pcyAqZnV0b24qIGFrYW4gc2VtYWtpbiBtdXJhaCBoYXJnYW55YSANCi0gKkNhbmNlbGxhdGlvbiBQb2xpY3kqDQotIEtvdGEgbGV0YWsgcHJvcGVydGkuIEtvdGEgQm9zdG9uIG1lbWlsaWtpIHJhdGEtcmF0YSBoYXJnYSBwcm9wZXJ0aSB0ZXJ0aW5nZ2kgZGFyaXBhZGEga2VzZWx1cnVoYW4gZGF0YQ0KLSBLZXRlcnNlZGlhYW4gZml0dXIgKmluc3RhbnRfYm9va2FibGUqLiBQcm9wZXJ0aSB5YW5nIHRpZGFrIG1lbWlsaWtpIGZpdHVyIHRlcnNlYnV0IGFrYW4gc2VtYWtpbiBtdXJhaA0KLSAqTGF0aXR1ZGUqICYgKmxvbmdpdHVkZSogcHJvcGVydGkuIFNlbWFraW4gcHJvcGVydGkgdGVybGV0YWsgZGkgZGFlcmFoIHRpbXVyICYgdXRhcmEgQVMsIHNlbWFraW4gbWFoYWwgaGFyZ2Egc2V3YW55YS4NCi0gU2VtYWtpbiB0aW5nZ2kgbmlsYWkgcmV2aWV3LCBzZW1ha2luIG1haGFsIGhhcmdhIHByb3BlcnRpbnlhLg0KLSBKdW1sYWgga2FtYXIgdGlkdXIgbWVsYW1iYW5na2FuIHNlbWFraW4gYmFueWFrbnlhIHRhbXUgeWFuZyBkYXBhdCBkaXRhbXB1bmcgZGFsYW0gcHJvcGVydGkuIFNlaGluZ2dhIGZpdHVyIGBiZWRyb29tc2AgbWVtcGVuZ2FydWhpIGhhcmdhIHByb3BlcnRpDQotIEp1bWxhaCB0ZW1wYXQgdGlkdXIgeWFuZyBkaXNlZGlha2FuIGFrYW4gbWVtcGVuZ2FydWhpIGhhcmdhIHByb3BlcnRpLiBTZW1ha2luIGJhbnlhayBqdW1sYWggdGVtcGF0IHRpZHVyLCBzZW1ha2luIHJlbmRhaCBoYXJnYW55YQ0KDQpVbnR1ayBwcm9wZXJ0aSBiYXJ1LCBmaXR1ciB5YW5nIG1lbXBlbmdhcnVoaSwgYW50YXJhIGxhaW4gOiANCg0KLSBUaXBlIHByb3BlcnRpDQotIEplbmlzIGthbWFyIChwcml2YXRlL3NoYXJlKQ0KLSBKdW1sYWggdGFtdSB5YW5nIGRhcGF0IGRpdGFtcHVuZyAoc2VtYWtpbiBiYW55YWsgc2VtYWtpbiB0aW5nZ2kgaGFyZ2Egc2V3YSBwcm9wZXJ0aSkNCi0gSnVtbGFoIGthbWFyIG1hbmRpIHRlcnNlZGlhDQotICpDYW5jZWxsYXRpb24gUG9saWN5Kg0KLSAqQ2xlYW5pbmcgRmVlKg0KLSBLb3RhIGxldGFrIHByb3BlcnRpDQotIEZpdHVyICppbnN0YW50IGJvb2thYmxlKiAoYXBhYmlsYSBhZGEsIHNlbWFraW4gbWVuZ3VyYW5naSBoYXJnYSBzZXdhKQ0KLSAqTGF0aXR1ZGUqICYgKmxvbmdpdHVkZSogcHJvcGVydGkuIFNlbWFraW4gcHJvcGVydGkgdGVybGV0YWsgZGkgZGFlcmFoIHRpbXVyICYgdXRhcmEgQVMsIHNlbWFraW4gbWFoYWwgaGFyZ2Egc2V3YW55YS4NCi0gSnVtbGFoIHJ1YW5nIHRpZHVyIGRhbiB0ZW1wYXQgdGlkdXIgbWVtcGVuZ2FydWhpIGhhcmdhIHByb3BlcnRpDQo=