Teori Risiko

Simulation and Resampling


Kontak : \(\downarrow\)
Email
Instagram yyosia
RPubs https://rpubs.com/yosia/

6.3 Cross Validation

Dalam bagian ini, kita akan mempelajari caranya:

  • Membandingkan dan membedakan validasi silang dengan teknik simulasi dan metode bootstrap.

  • Menggunakan teknik validasi silang untuk pemilihan model

  • Menjelaskan metode jackknife sebagai kasus khusus validasi silang dan menghitung estimasi bias dan kesalahan standar jackknife

Validasi silang, yang diperkenalkan secara singkat pada Bagian 4.2.4, adalah teknik yang didasarkan pada hasil simulasi. Sekarang kita akan membandingkan dan membedakan validasi silang dengan teknik simulasi lain yang telah diperkenalkan dalam bab ini.”

  • Simulasi, atau Monte-Carlo, yang diperkenalkan pada Bagian 6.1, memungkinkan kita untuk menghitung nilai ekspektasi dan rangkuman distribusi statistik lainnya, seperti nilai-p, dengan mudah.

  • Bootstrap, dan metode resampling lainnya yang diperkenalkan pada Bagian 6.2, menyediakan estimator presisi, atau variabilitas, statistik.

  • Validasi silang penting ketika menilai seberapa akurat model prediktif akan bekerja dalam praktiknya.

Tumpang tindih memang ada, namun tetap saja akan sangat membantu untuk memikirkan tujuan luas yang terkait dengan setiap metode statistik.

Untuk membahas validasi silang, mari kita ingat kembali dari Bagian 4.2 beberapa ide kunci dari validasi model. Ketika menilai, atau memvalidasi, sebuah model, kita melihat kinerja yang diukur pada data baru, atau setidaknya bukan data yang digunakan untuk mencocokkan model. Pendekatan klasik, yang dijelaskan di Bagian 4.2.3, adalah membagi sampel menjadi dua: satu bagian (dataset pelatihan) digunakan untuk menyesuaikan model dan bagian lainnya (dataset pengujian) digunakan untuk memvalidasi. Namun, keterbatasan dari pendekatan ini adalah bahwa hasilnya bergantung pada pembagian; meskipun keseluruhan sampel tetap, pembagian antara sub-sampel pelatihan dan pengujian bervariasi secara acak. Sampel pelatihan yang berbeda berarti parameter estimasi model akan berbeda. Parameter model yang berbeda dan sampel uji yang berbeda berarti statistik validasi akan berbeda. Dua orang analis dapat menggunakan data yang sama dan model yang sama, namun mencapai kesimpulan yang berbeda tentang kelayakan suatu model (berdasarkan pembagian acak yang berbeda), sebuah situasi yang membuat frustasi.

6.3.1 k-Fold Cross-Validation

Untuk mengurangi kesulitan ini, biasanya digunakan pendekatan validasi silang seperti yang diperkenalkan di Bagian 4.2.4. Ide utamanya adalah meniru pendekatan pengujian/pelatihan dasar untuk validasi model dengan mengulanginya berkali-kali melalui rata-rata dari beberapa bagian data yang berbeda. Keuntungan utamanya adalah bahwa statistik validasi tidak terikat pada model parametrik (atau nonparametrik) tertentu - seseorang dapat menggunakan statistik nonparametrik atau statistik yang memiliki interpretasi ekonomi - sehingga dapat digunakan untuk membandingkan model yang tidak bersarang (tidak seperti prosedur rasio kemungkinan).

Contoh 6.3.1. Dana Properti Wisconsin. Untuk data dana properti 2010 yang diperkenalkan pada Bagian 1.3, kami mencocokkan distribusi gamma dan Pareto dengan 1.377 data klaim. Untuk rincian kecocokan terkait, lihat Lampiran Bagian 15.4.4. Sekarang kita mempertimbangkan statistik Kolmogorov-Smirnov yang diperkenalkan di Bagian 4.1.2.2. Ketika seluruh dataset telah sesuai, statistik kecocokan Kolmogorov-Smirnov untuk distribusi gamma adalah 0,2639 dan untuk distribusi Pareto adalah 0,0478. Nilai yang lebih rendah untuk distribusi Pareto menunjukkan bahwa distribusi ini lebih cocok daripada gamma.

Untuk melihat bagaimana validasi silang k-lipatan bekerja, kami membagi data secara acak menjadi \(k=8\) kelompok, atau lipatan, yang masing-masing memiliki sekitar \(1377/8≈172\) pengamatan. Kemudian, kami mencocokkan model gamma dan Pareto pada set data dengan tujuh lipatan pertama (sekitar $172⋅7 = 120$4 pengamatan), menentukan estimasi parameter, dan kemudian menggunakan model-model yang cocok dengan data yang ditahan untuk menentukan statistik Kolmogorov-Smirnov.

library(VGAM)
## Loading required package: stats4
## Loading required package: splines
library(MASS)
claim_lev <- read.csv("CLAIMLEVEL.csv", header = TRUE) 
claim_data <- subset(claim_lev, Year == 2010); 

# Randomly re-order the data - "shuffle it"
n <- nrow(claim_data)
set.seed(12347)
cvdata <- claim_data[sample(n), ]
# Number of folds
k <- 8
cvalvec <- matrix(0,2,k)
for (i in 1:k) {
  indices <- (((i-1) * round((1/k)*nrow(cvdata))) + 1):((i*round((1/k) * nrow(cvdata))))
# Pareto
  fit.pareto <- vglm(Claim ~ 1, paretoII, loc = 0, data = cvdata[-indices,])
  ksResultPareto <- ks.test(cvdata[indices,]$Claim, "pparetoII", loc = 0, shape = exp(coef(fit.pareto)[2]), 
        scale = exp(coef(fit.pareto)[1]))
  cvalvec[1,i] <- ksResultPareto$statistic
# Gamma
  fit.gamma <- glm(Claim ~ 1, data = cvdata[-indices,], family = Gamma(link = log)) 
  gamma_theta <- exp(coef(fit.gamma)) * gamma.dispersion(fit.gamma)  
  alpha <- 1 / gamma.dispersion(fit.gamma)
  ksResultGamma <- ks.test(cvdata[indices,]$Claim, "pgamma", shape = alpha, scale = gamma_theta)
  cvalvec[2,i] <- ksResultGamma$statistic
}
KScv <- rowSums(cvalvec)/k

Hasilnya tampak pada Gambar 6.12 di mana sumbu horizontal adalah Fold=1. Proses ini diulangi untuk tujuh lipatan lainnya. Hasil yang dirangkum dalam Gambar 6.12 menunjukkan bahwa Pareto secara konsisten memberikan distribusi prediktif yang lebih dapat diandalkan daripada gamma.

# Plot the statistics
matplot(1:k,t(cvalvec),type="b", col=c(1,3), lty=1:2, 
        ylim=c(0,0.4), pch = 0, xlab="Fold", ylab="KS Statistic")
legend("left", c("Pareto", "Gamma"), col=c(1,3),lty=1:2, bty="n")

“Figure 6.2:” Statistik Kolmogorov-Smirnov (KS) yang telah divalidasi silang untuk Data Klaim Dana Asuransi. Garis hitam solid untuk distribusi Pareto, garis putus-putus hijau untuk distribusi gamma. Statistik KS mengukur deviasi terbesar antara distribusi yang sesuai dengan distribusi empiris untuk masing-masing dari 8 kelompok, atau lipatan, data yang dipilih secara acak.

6.3.2 Leave-One-Out Cross-Validation

Kasus khusus di mana \(k=n\) dikenal sebagai validasi silang tinggalkan-satu-keluar. Kasus ini secara historis sangat menonjol dan terkait erat dengan jackknifestatistik yang merupakan pendahulu dari teknik bootstrap.

Meskipun kita menyajikannya sebagai kasus khusus validasi silang, akan sangat membantu jika kami memberikan definisi eksplisit. Pertimbangkan sebuah statistik umum \(θˆ = t(x)\) yang merupakan penaksir untuk sebuah parameter yang diminati \(θ\). Ide dari jackknife adalah menghitung n nilai \(θˆ_{-i} = t(x-i)\), di mana \(x-i\) adalah subsampel dari \(x\) dengan nilai \(ke-i\) dihilangkan. Rata-rata dari nilai-nilai ini dilambangkan sebagai

\[\overline{\widehat{\theta}}_{(\cdot)}=\frac{1}{n}\sum_{i=1}^n \widehat{\theta}_{-i} .\]

Nilai-nilai ini dapat digunakan untuk membuat estimasi bias dari statistik \(\hatθ\)

\[\begin{equation} Bias_{jack} = (n-1) \left(\overline{\widehat{\theta}}_{(\cdot)} - \widehat{\theta}\right) \tag{6.3} \end{equation}\]

serta estimasi standar deviasi

\[\begin{equation} s_{jack} =\sqrt{\frac{n-1}{n}\sum_{i=1}^n \left(\widehat{\theta}_{-i} -\overline{\widehat{\theta}}_{(\cdot)}\right)^2} ~. \tag{6.4} \end{equation}\]

Contoh 6.3.2. Koefisien Variasi. Sebagai ilustrasi, pertimbangkan sebuah sampel fiktif kecil \(x = {x_1,...,x_n}\) dengan realisasi

sample_x <- c(2.46,2.80,3.28,3.86,2.85,3.67,3.37,3.40,
              5.22,2.55,2.79,4.50,3.37,2.88,1.44,2.56,2.00,2.07,2.19,1.77)

Misalkan kita tertarik dengan \(\theta = CV = \sqrt{\mathrm{Var~}[X]}/\mathrm{E~}[X]\)

Dengan dataset ini, estimator koefisien variasi menjadi 0,31196. Namun, seberapa handalkah estimasi tersebut? Untuk menjawab pertanyaan ini, kita dapat menghitung estimator pisau lipat dari bias dan deviasi standarnya. Kode berikut ini menunjukkan bahwa penaksir jackknife untuk bias adalah \(Bias_{jack} = -0,00627\) dan standar deviasi jackknife adalah \(s_{jack} = 0,01293\).

CVar <- function(x) sqrt(var(x))/mean(x)
JackCVar <- function(i) sqrt(var(sample_x[-i]))/mean(sample_x[-i])
JackTheta <- Vectorize(JackCVar)(1:length(sample_x))
BiasJack <- (length(sample_x)-1)*(mean(JackTheta) - CVar(sample_x))
sd(JackTheta)
## [1] 0.01293001

Contoh 6.3.3. Klaim Cidera Badan dan Rasio Eliminasi Kerugian. Pada Contoh 6.2.1, kita telah menunjukkan bagaimana menghitung estimasi bootstrap dari bias dan deviasi standar untuk rasio eliminasi kerugian dengan menggunakan data klaim cedera badan pada Contoh 4.1.11. Sekarang kita menindaklanjuti dengan memberikan jumlah yang sebanding dengan menggunakan statistik jackknife.

Tabel 6.7 merangkum hasil estimasi jackknife. Tabel ini menunjukkan bahwa estimasi jackknife terhadap bias dan deviasi standar dari rasio eliminasi kerugian \(E [min (X, d)]/E [X]\) sebagian besar konsisten dengan metodologi bootstrap. Selain itu, kita dapat menggunakan standar deviasi untuk membangun interval kepercayaan berbasis normal, yang berpusat di sekitar penaksir yang dikoreksi bias. Sebagai contoh, pada \(d = 14000\), kita melihat pada Contoh 4.1.11 bahwa estimasi nonparametrik dari \(LER\) adalah 0.97678. Estimasi ini memiliki bias sebesar 0,00010, sehingga menghasilkan estimator terkoreksi-bias sebesar 0,97688. Interval kepercayaan 95% dihasilkan dengan membuat interval dua kali panjang 1,96 deviasi standar jackknife, yang berpusat pada estimator terkoreksi bias (1,96 adalah perkiraan kuantil ke-97,5 dari distribusi normal standar).

library(boot)
## 
## Attaching package: 'boot'
## The following objects are masked from 'package:VGAM':
## 
##     logit, simplex
# Example from Derrig et al
BIData <- read.csv("DerrigResampling.csv", header =T)
BIData$Censored <- 1*(BIData$AmountPaid >= BIData$PolicyLimit)
BIDataUncensored <- subset(BIData, Censored == 0)
LER.boot <- function(ded, data, indices){
  resample.data <- data[indices,]
  sumClaims <- sum(resample.data$AmountPaid)
  sumClaims_d <- sum(pmin(resample.data$AmountPaid,ded))
  LER <-   sumClaims_d/sumClaims
  return(LER)  
}

x <- BIDataUncensored$AmountPaid
LER.jack<- function(ded,i){
  LER <-   sum(pmin(x[-i],ded))/sum(x[-i])
  return(LER)  
}
LER <- function(ded) sum(pmin(x,ded))/sum(x)
##Derrig et al
set.seed(2019)
dVec2 <- c(4000, 5000, 10500, 11500, 14000, 18500)
OutJack <- matrix(0,length(dVec2),8)
  for (j in 1:length(dVec2)) {
OutJack[j,1] <- dVec2[j]
results <- boot(data=BIDataUncensored, statistic=LER.boot, R=1000, ded=dVec2[j])
OutJack[j,2] <- results$t0
biasboot <- mean(results$t)-results$t0 -> OutJack[j,3]
sdboot <- sd(results$t) -> OutJack[j,4]
temp <- boot.ci(results)

LER.jack.ded<- function(i) LER.jack(ded=dVec2[j],i)
JackTheta.ded <- Vectorize(LER.jack.ded)(1:length(x))
OutJack[j,5] <- BiasJack.ded <- (length(x)-1)*(mean(JackTheta.ded) - LER(ded=dVec2[j]))
OutJack[j,6] <- sd(JackTheta.ded)
OutJack[j,7:8] <- mean(JackTheta.ded)+qt(c(0.025,0.975),length(x)-1)*OutJack[j,6]
  }

Table 6.7. Estimasi Jackknife dari LER pada Deductible yang Dipilih

d NP Estimate Bootstrap Bias Bootstrap SD Jackknife Bias Jackknife SD Lower Jackknife 95% CI Upper Jackknife 95% CI
4000 0.54113 0.00011 0.01237 0.00031 0.00061 0.53993 0.54233
5000 0.64960 0.00027 0.01412 0.00033 0.00068 0.64825 0.65094
10500 0.93563 0.00004 0.01017 0.00019 0.00053 0.93460 0.93667
11500 0.95281 -0.00003 0.00941 0.00016 0.00047 0.95189 0.95373
14000 0.97678 0.00016 0.00687 0.00010 0.00034 0.97612 0.97745
18500 0.99382 0.00014 0.00331 0.00003 0.00017 0.99350 0.99415

Diskusi. Salah satu dari banyak hal menarik tentang kasus khusus leave-one-out adalah kemampuan untuk mereplikasi estimasi dengan tepat. Artinya, ketika ukuran lipatan hanya satu, maka tidak ada ketidakpastian tambahan yang disebabkan oleh validasi silang. Ini berarti bahwa para analis dapat mereplikasi pekerjaan satu sama lain dengan tepat, sebuah pertimbangan yang penting.

Statistik Jackknife dikembangkan untuk memahami ketepatan estimator, menghasilkan estimator bias dan deviasi standar pada persamaan (6.3) dan (6.4). Hal ini sesuai dengan tujuan yang telah kita kaitkan dengan teknik bootstrap, bukan metode validasi silang. Hal ini menunjukkan bagaimana teknik statistik dapat digunakan untuk mencapai tujuan yang berbeda.

6.3.3 Cross-Validation and Bootstrap

Bootstrap berguna untuk memberikan estimator presisi, atau variabilitas, dari statistik. Hal ini juga berguna untuk validasi model. Pendekatan bootstrap untuk validasi model mirip dengan prosedur validasi leave-one-out dan k-fold:

  • Buat sampel bootstrap dengan mengambil sampel ulang (dengan penggantian) \(n\) indeks dalam \({1, ⋯, n}\). Ini akan menjadi sampel pelatihan kita. Perkirakan model yang sedang dipertimbangkan berdasarkan sampel ini.

  • Uji, atau sampel validasi, terdiri dari pengamatan yang tidak dipilih untuk pelatihan. Mengevaluasi model yang cocok (berdasarkan data pelatihan) dengan menggunakan data uji.

Ulangi proses ini beberapa kali (katakanlah \(B\)). Ambil rata-rata dari hasil-hasilnya dan pilih model berdasarkan statistik evaluasi rata-rata.

Contoh 6.3.4. Dana Properti Wisconsin. Kembali ke Contoh 6.3.1 di mana kita menyelidiki kecocokan distribusi gamma dan Pareto pada data dana properti. Kita kembali membandingkan kinerja prediksi menggunakan statistik Kolmogorov-Smirnov (KS), namun kali ini menggunakan prosedur bootstrap untuk membagi data antara sampel pelatihan dan pengujian. Berikut ini adalah kode ilustrasinya.

library(goftest)
n <- nrow(claim_data)
set.seed(12347)
indices <- 1:n
# Number of Bootstrap Samples
B <- 100
cvalvec <- matrix(0,2,B)
for (i in 1:B) {
  bootindex <- unique(sample(indices, size=n, replace= TRUE))
  traindata <- claim_data[bootindex,]
  testdata  <- claim_data[-bootindex,]
# Pareto
  fit.pareto <- vglm(Claim ~ 1, paretoII, loc = 0, data = traindata)
  ksResultPareto <- ks.test(testdata$Claim, "pparetoII", loc = 0, shape = exp(coef(fit.pareto)[2]), 
        scale = exp(coef(fit.pareto)[1]))
  cvalvec[1,i] <- ksResultPareto$statistic
# Gamma
  fit.gamma <- glm(Claim ~ 1, data = traindata, family = Gamma(link = log)) 
  gamma_theta <- exp(coef(fit.gamma)) * gamma.dispersion(fit.gamma)  
  alpha <- 1 / gamma.dispersion(fit.gamma)
  ksResultGamma <- ks.test(testdata$Claim, "pgamma", shape = alpha, scale = gamma_theta)
  cvalvec[2,i] <- ksResultGamma$statistic
}
KSBoot <- rowSums(cvalvec)/B

Kami melakukan pengambilan sampel dengan menggunakan B= 100 ulangan. Statistik KS rata-rata untuk distribusi Pareto adalah 0,058 dibandingkan dengan rata-rata untuk distribusi gamma, 0,262. Hal ini konsisten dengan hasil sebelumnya dan memberikan bukti lain bahwa Pareto adalah model yang lebih baik untuk data ini dibandingkan dengan gamma.

6.4 Importance Sampling

Bagian 6.1 memperkenalkan teknik Monte Carlo dengan menggunakan teknik inversi: untuk membangkitkan sebuah variabel acak \(X\) dengan distribusi \(F\), terapkan \(F^{-1}\) pada pemanggilan sebuah generator acak (seragam pada interval satuan). Bagaimana jika kita ingin menggambar sesuai dengan \(X\), dengan syarat \(X∈[a,b]\)?

Seseorang dapat menggunakan mekanisme terima-tolak: menarik \(x\) dari distribusi \(F\)

  • jika \(x\in[a,b]\): simpan (“terima”)

  • jika \(x\notin[a,b]\): gambar yang lain (“tolak”)

Amati bahwa dari n nilai yang awalnya dihasilkan, kita simpan di sini hanya \([F(b)-F(a)] ⋅ n\) hasil imbang, rata-rata.

Contoh 6.4.1. Penarikan dari Distribusi Normal. Misalkan kita menggambar dari distribusi normal dengan rata-rata 2,5 dan varians 1, \(N(2,5,1)\), tetapi hanya tertarik pada gambar yang lebih besar dari \(a≥2\) dan kurang dari \(b≤4\). Artinya, kita hanya dapat menggunakan \(F(4)-F(2)=Φ(4-2.5)-Φ(2-2.5) = 0.9332 - 0.3085 = 0.6247\) proporsi undian. Gambar 6.13 menunjukkan bahwa beberapa hasil undian berada di dalam interval \((2,4)\) dan beberapa di luarnya.

mu = 2.5
sigma = 1
a = 2
b = 4
Fa = pnorm(a,mu,sigma)
Fb = pnorm(b,mu,sigma)
pic_ani = function(){
  u=seq(0,5,by=.01)
  plot(u,pnorm(u,mu,sigma),col="white",ylab="",xlab="")
  rect(-1,-1,6,2,col=rgb(1,0,0,.2),border=NA)
  rect(a,Fa,b,Fb,col="white",border=NA)
  lines(u,pnorm(u,mu,sigma),lwd=2)
  abline(v=c(a,b),lty=2,col="red")
  ru <- runif(1)
  clr <- "red"
  if((qnorm(ru,mu,sigma)>=a)&(qnorm(ru,mu,sigma)<=b)) clr <- "blue"
  segments(-1,ru,qnorm(ru,mu,sigma),ru,col=clr,lwd=2)
  arrows(qnorm(ru,mu,sigma),ru,qnorm(ru,mu,sigma),0,col=clr,lwd=2,length = .1)
}
for (i in 1:numAnimation) {pic_ani()}


Sebagai gantinya, seseorang dapat menggambar menurut distribusi bersyarat \(F^⋆\) yang didefinisikan sebagai

\[F^{\star}(x) = \Pr(X \le x | a < X \le b) =\frac{F(x)-F(a)}{F(b)-F(a)}, \ \ \ \text{for } a < x \le b .\] Dengan menggunakan metode inverse transform pada Bagian 6.1.2, kita mendapatkan hasil imbang

\[X^\star=F^{\star-1}\left( U \right) = F^{-1}\left(F(a)+U\cdot[F(b)-F(a)]\right)\]

memiliki distribusi \(F⋆^\). Dinyatakan dengan cara lain, definisikan

\[\tilde{U} = (1-U)\cdot F(a)+U\cdot F(b)\]

dan kemudian gunakan \(F^{-1}(\tilde{U})\). Dengan pendekatan ini, setiap undian dihitung.

Hal ini dapat dikaitkan dengan mekanisme pengambilan sampel kepentingan: kita menarik lebih sering di wilayah yang kita harapkan memiliki kuantitas yang memiliki kepentingan. Transformasi ini dapat dianggap sebagai “perubahan ukuran.”

pic_ani = function(){
  u=seq(0,5,by=.01)
  plot(u,pnorm(u,mu,sigma),col="white",ylab="",xlab="")
  rect(-1,-1,6,2,col=rgb(1,0,0,.2),border=NA)
  rect(a,Fa,b,Fb,col="white",border=NA)
  lines(u,pnorm(u,mu,sigma),lwd=2)
  abline(h=pnorm(c(a,b),mu,sigma),lty=2,col="red")
  ru <- runif(1)
  rutilde <- (1-ru)*Fa+ru*Fb
  segments(-1,rutilde,qnorm(rutilde,mu,sigma),rutilde,col="blue",lwd=2)
  arrows(qnorm(rutilde,mu,sigma),rutilde,qnorm(rutilde,mu,sigma),0,col="blue",lwd=2,length = .1)
}
for (i in 1:numAnimation) {pic_ani()}


Pada Contoh 6.4.1., kebalikan dari distribusi normal sudah tersedia (dalam R, fungsinya adalah qnorm). Namun, untuk aplikasi lain, hal ini tidak terjadi. Kemudian, kita cukup menggunakan metode numerik untuk menentukan \(X^⋆\) sebagai solusi dari persamaan \(F(X^\star) =\tilde{U}\) di mana \(\tilde{U}=(1-U)\cdot F(a)+U\cdot F(b)\)). Lihat kode ilustrasi berikut ini.

pic_ani = function(){
  u=seq(0,5,by=.01)
  plot(u,pnorm(u,mu,sigma),col="white",ylab="",xlab="")
  rect(-1,-1,6,2,col=rgb(1,0,0,.2),border=NA)
  rect(2,-1,4,2,col="white",border=NA)
  lines(u,pnorm(u,mu,sigma),lty=2)
  pnormstar <- Vectorize(function(x){
    y=(pnorm(x,mu,sigma)-Fa)/(Fb-Fa)
    if(x<=a) y <- 0
    if(x>=b) y <- 1
    return(y)
    })
  qnormstar <- function(u) as.numeric(uniroot((function (x) pnormstar(x) - u), lower = 2, upper = 4)[1])
  lines(u,pnormstar(u),lwd=2)
  abline(v=c(2,4),lty=2,col="red")
  ru <- runif(1)
  segments(-1,ru,qnormstar(ru),ru,col="blue",lwd=2)
  arrows(qnormstar(ru),ru,qnormstar(ru),0,col="blue",lwd=2,length = .1)
}
for (i in 1:numAnimation) {pic_ani()}


LS0tDQp0aXRsZTogIlRlb3JpIFJpc2lrbyINCnN1YnRpdGxlOiAiU2ltdWxhdGlvbiBhbmQgUmVzYW1wbGluZyINCmF1dGhvcjogIllvc2lhIg0KZGF0ZTogICIxMy8wMy8yMDIzIg0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpyb2JvYm9vazogICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9qdWJhL3JtZGZvcm1hdHMNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICB0aGVtZTogc2FuZHN0b25lDQogICAgdGh1bWJuYWlsczogdHJ1ZQ0KICAgIGxpZ2h0Ym94OiB0cnVlDQogICAgZ2FsbGVyeTogdHJ1ZQ0KICAgIGxpYl9kaXI6IGxpYnMNCiAgICBkZl9wcmludDogInBhZ2VkIg0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY3NzOiAic3R5bGUuY3NzIg0KDQotLS0NCg0KPEJPRFkgew0KIHVzZXItc2VsZWN0Om5vbmU7DQogLW1vei11c2VyLXNlbGVjdDpub25lOw0KIC1tcy11c2VyLXNlbGVjdDpub25lOw0KIC1raHRtbC11c2VyLXNlbGVjdDpub25lOw0KIC13ZWJraXQtdXNlci1zZWxlY3Q6bm9uZQ0KfS8+DQoNCjxicj4NCg0KPGltZyBzdHlsZT0iZmxvYXQ6IHJpZ2h0OyBtYXJnaW46IC01MHB4IDUwcHggMHB4IDUwcHg7IHdpZHRoOjMwJSIgc3JjPSJkb3dubG9hZC5wbmciLz4gDQoNCnwNCjotLS0tIHw6LS0tLQ0KKipLb250YWsqKnwgKio6ICRcZG93bmFycm93JCoqDQpFbWFpbHwgeW9zaWEueW9zaWFAc3R1ZGVudC5tYXRhbmF1bml2ZXJzaXR5LmFjLmlkDQpJbnN0YWdyYW0gfCBbeXlvc2lhXShodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL3l5b3NpYS8pIA0KUlB1YnMgIHwgaHR0cHM6Ly9ycHVicy5jb20veW9zaWEvDQoNCioqKg0KDQojIDYuMyBDcm9zcyBWYWxpZGF0aW9uICAgIA0KDQpEYWxhbSBiYWdpYW4gaW5pLCBraXRhIGFrYW4gbWVtcGVsYWphcmkgY2FyYW55YToNCg0KKiBNZW1iYW5kaW5na2FuIGRhbiBtZW1iZWRha2FuIHZhbGlkYXNpIHNpbGFuZyBkZW5nYW4gdGVrbmlrIHNpbXVsYXNpIGRhbiBtZXRvZGUgYm9vdHN0cmFwLg0KDQoqCU1lbmdndW5ha2FuIHRla25payB2YWxpZGFzaSBzaWxhbmcgdW50dWsgcGVtaWxpaGFuIG1vZGVsDQoNCioJTWVuamVsYXNrYW4gbWV0b2RlIGphY2trbmlmZSBzZWJhZ2FpIGthc3VzIGtodXN1cyB2YWxpZGFzaSBzaWxhbmcgZGFuIG1lbmdoaXR1bmcgZXN0aW1hc2kgYmlhcyBkYW4ga2VzYWxhaGFuIHN0YW5kYXIgamFja2tuaWZlDQoNClZhbGlkYXNpIHNpbGFuZywgeWFuZyBkaXBlcmtlbmFsa2FuIHNlY2FyYSBzaW5na2F0IHBhZGEgQmFnaWFuIDQuMi40LCBhZGFsYWggdGVrbmlrIHlhbmcgZGlkYXNhcmthbiBwYWRhIGhhc2lsIHNpbXVsYXNpLiBTZWthcmFuZyBraXRhIGFrYW4gbWVtYmFuZGluZ2thbiBkYW4gbWVtYmVkYWthbiB2YWxpZGFzaSBzaWxhbmcgZGVuZ2FuIHRla25payBzaW11bGFzaSBsYWluIHlhbmcgdGVsYWggZGlwZXJrZW5hbGthbiBkYWxhbSBiYWIgaW5pLiINCg0KKglTaW11bGFzaSwgYXRhdSBNb250ZS1DYXJsbywgeWFuZyBkaXBlcmtlbmFsa2FuIHBhZGEgQmFnaWFuIDYuMSwgbWVtdW5na2lua2FuIGtpdGEgdW50dWsgbWVuZ2hpdHVuZyBuaWxhaSBla3NwZWt0YXNpIGRhbiByYW5na3VtYW4gZGlzdHJpYnVzaSBzdGF0aXN0aWsgbGFpbm55YSwgc2VwZXJ0aSBuaWxhaS1wLCBkZW5nYW4gbXVkYWguDQoNCiogQm9vdHN0cmFwLCBkYW4gbWV0b2RlIHJlc2FtcGxpbmcgbGFpbm55YSB5YW5nIGRpcGVya2VuYWxrYW4gcGFkYSBCYWdpYW4gNi4yLCBtZW55ZWRpYWthbiBlc3RpbWF0b3IgcHJlc2lzaSwgYXRhdSB2YXJpYWJpbGl0YXMsIHN0YXRpc3Rpay4NCg0KKglWYWxpZGFzaSBzaWxhbmcgcGVudGluZyBrZXRpa2EgbWVuaWxhaSBzZWJlcmFwYSBha3VyYXQgbW9kZWwgcHJlZGlrdGlmIGFrYW4gYmVrZXJqYSBkYWxhbSBwcmFrdGlrbnlhLg0KDQpUdW1wYW5nIHRpbmRpaCBtZW1hbmcgYWRhLCBuYW11biB0ZXRhcCBzYWphIGFrYW4gc2FuZ2F0IG1lbWJhbnR1IHVudHVrIG1lbWlraXJrYW4gdHVqdWFuIGx1YXMgeWFuZyB0ZXJrYWl0IGRlbmdhbiBzZXRpYXAgbWV0b2RlIHN0YXRpc3Rpay4NCiAgICANClVudHVrIG1lbWJhaGFzIHZhbGlkYXNpIHNpbGFuZywgbWFyaSBraXRhIGluZ2F0IGtlbWJhbGkgZGFyaSBCYWdpYW4gNC4yIGJlYmVyYXBhIGlkZSBrdW5jaSBkYXJpIHZhbGlkYXNpIG1vZGVsLiBLZXRpa2EgbWVuaWxhaSwgYXRhdSBtZW12YWxpZGFzaSwgc2VidWFoIG1vZGVsLCBraXRhIG1lbGloYXQga2luZXJqYSB5YW5nIGRpdWt1ciBwYWRhIGRhdGEgYmFydSwgYXRhdSBzZXRpZGFrbnlhIGJ1a2FuIGRhdGEgeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuY29jb2trYW4gbW9kZWwuIFBlbmRla2F0YW4ga2xhc2lrLCB5YW5nIGRpamVsYXNrYW4gZGkgQmFnaWFuIDQuMi4zLCBhZGFsYWggbWVtYmFnaSBzYW1wZWwgbWVuamFkaSBkdWE6IHNhdHUgYmFnaWFuIChkYXRhc2V0IHBlbGF0aWhhbikgZGlndW5ha2FuIHVudHVrIG1lbnllc3VhaWthbiBtb2RlbCBkYW4gYmFnaWFuIGxhaW5ueWEgKGRhdGFzZXQgcGVuZ3VqaWFuKSBkaWd1bmFrYW4gdW50dWsgbWVtdmFsaWRhc2kuIE5hbXVuLCBrZXRlcmJhdGFzYW4gZGFyaSBwZW5kZWthdGFuIGluaSBhZGFsYWggYmFod2EgaGFzaWxueWEgYmVyZ2FudHVuZyBwYWRhIHBlbWJhZ2lhbjsgbWVza2lwdW4ga2VzZWx1cnVoYW4gc2FtcGVsIHRldGFwLCBwZW1iYWdpYW4gYW50YXJhIHN1Yi1zYW1wZWwgcGVsYXRpaGFuIGRhbiBwZW5ndWppYW4gYmVydmFyaWFzaSBzZWNhcmEgYWNhay4gU2FtcGVsIHBlbGF0aWhhbiB5YW5nIGJlcmJlZGEgYmVyYXJ0aSBwYXJhbWV0ZXIgZXN0aW1hc2kgbW9kZWwgYWthbiBiZXJiZWRhLiBQYXJhbWV0ZXIgbW9kZWwgeWFuZyBiZXJiZWRhIGRhbiBzYW1wZWwgdWppIHlhbmcgYmVyYmVkYSBiZXJhcnRpIHN0YXRpc3RpayB2YWxpZGFzaSBha2FuIGJlcmJlZGEuIER1YSBvcmFuZyBhbmFsaXMgZGFwYXQgbWVuZ2d1bmFrYW4gZGF0YSB5YW5nIHNhbWEgZGFuIG1vZGVsIHlhbmcgc2FtYSwgbmFtdW4gbWVuY2FwYWkga2VzaW1wdWxhbiB5YW5nIGJlcmJlZGEgdGVudGFuZyBrZWxheWFrYW4gc3VhdHUgbW9kZWwgKGJlcmRhc2Fya2FuIHBlbWJhZ2lhbiBhY2FrIHlhbmcgYmVyYmVkYSksIHNlYnVhaCBzaXR1YXNpIHlhbmcgbWVtYnVhdCBmcnVzdGFzaS4NCg0KIyMgNi4zLjEgay1Gb2xkIENyb3NzLVZhbGlkYXRpb24NCg0KVW50dWsgbWVuZ3VyYW5naSBrZXN1bGl0YW4gaW5pLCBiaWFzYW55YSBkaWd1bmFrYW4gcGVuZGVrYXRhbiB2YWxpZGFzaSBzaWxhbmcgc2VwZXJ0aSB5YW5nIGRpcGVya2VuYWxrYW4gZGkgQmFnaWFuIDQuMi40LiBJZGUgdXRhbWFueWEgYWRhbGFoIG1lbmlydSBwZW5kZWthdGFuIHBlbmd1amlhbi9wZWxhdGloYW4gZGFzYXIgdW50dWsgdmFsaWRhc2kgbW9kZWwgZGVuZ2FuIG1lbmd1bGFuZ2lueWEgYmVya2FsaS1rYWxpIG1lbGFsdWkgcmF0YS1yYXRhIGRhcmkgYmViZXJhcGEgYmFnaWFuIGRhdGEgeWFuZyBiZXJiZWRhLiBLZXVudHVuZ2FuIHV0YW1hbnlhIGFkYWxhaCBiYWh3YSBzdGF0aXN0aWsgdmFsaWRhc2kgdGlkYWsgdGVyaWthdCBwYWRhIG1vZGVsIHBhcmFtZXRyaWsgKGF0YXUgbm9ucGFyYW1ldHJpaykgdGVydGVudHUgLSBzZXNlb3JhbmcgZGFwYXQgbWVuZ2d1bmFrYW4gc3RhdGlzdGlrIG5vbnBhcmFtZXRyaWsgYXRhdSBzdGF0aXN0aWsgeWFuZyBtZW1pbGlraSBpbnRlcnByZXRhc2kgZWtvbm9taSAtIHNlaGluZ2dhIGRhcGF0IGRpZ3VuYWthbiB1bnR1ayBtZW1iYW5kaW5na2FuIG1vZGVsIHlhbmcgdGlkYWsgYmVyc2FyYW5nICh0aWRhayBzZXBlcnRpIHByb3NlZHVyIHJhc2lvIGtlbXVuZ2tpbmFuKS4NCg0KKipDb250b2ggNi4zLjEuIERhbmEgUHJvcGVydGkgV2lzY29uc2luLioqIFVudHVrIGRhdGEgZGFuYSBwcm9wZXJ0aSAyMDEwIHlhbmcgZGlwZXJrZW5hbGthbiBwYWRhIEJhZ2lhbiAxLjMsIGthbWkgbWVuY29jb2trYW4gZGlzdHJpYnVzaSBnYW1tYSBkYW4gUGFyZXRvIGRlbmdhbiAxLjM3NyBkYXRhIGtsYWltLiBVbnR1ayByaW5jaWFuIGtlY29jb2thbiB0ZXJrYWl0LCBsaWhhdCBMYW1waXJhbiBCYWdpYW4gMTUuNC40LiBTZWthcmFuZyBraXRhIG1lbXBlcnRpbWJhbmdrYW4gc3RhdGlzdGlrIEtvbG1vZ29yb3YtU21pcm5vdiB5YW5nIGRpcGVya2VuYWxrYW4gZGkgQmFnaWFuIDQuMS4yLjIuIEtldGlrYSBzZWx1cnVoIGRhdGFzZXQgdGVsYWggc2VzdWFpLCBzdGF0aXN0aWsga2Vjb2Nva2FuIEtvbG1vZ29yb3YtU21pcm5vdiB1bnR1ayBkaXN0cmlidXNpIGdhbW1hIGFkYWxhaCAwLDI2MzkgZGFuIHVudHVrIGRpc3RyaWJ1c2kgUGFyZXRvIGFkYWxhaCAwLDA0NzguIE5pbGFpIHlhbmcgbGViaWggcmVuZGFoIHVudHVrIGRpc3RyaWJ1c2kgUGFyZXRvIG1lbnVuanVra2FuIGJhaHdhIGRpc3RyaWJ1c2kgaW5pIGxlYmloIGNvY29rIGRhcmlwYWRhIGdhbW1hLg0KDQpVbnR1ayBtZWxpaGF0IGJhZ2FpbWFuYSB2YWxpZGFzaSBzaWxhbmcgay1saXBhdGFuIGJla2VyamEsIGthbWkgbWVtYmFnaSBkYXRhIHNlY2FyYSBhY2FrIG1lbmphZGkgJGs9OCQga2Vsb21wb2ssIGF0YXUgbGlwYXRhbiwgeWFuZyBtYXNpbmctbWFzaW5nIG1lbWlsaWtpIHNla2l0YXIgJDEzNzcvOOKJiDE3MiQgcGVuZ2FtYXRhbi4gS2VtdWRpYW4sIGthbWkgbWVuY29jb2trYW4gbW9kZWwgZ2FtbWEgZGFuIFBhcmV0byBwYWRhIHNldCBkYXRhIGRlbmdhbiB0dWp1aCBsaXBhdGFuIHBlcnRhbWEgKHNla2l0YXIgJDE3MuKLhTcgPSAxMjAkNCBwZW5nYW1hdGFuKSwgbWVuZW50dWthbiBlc3RpbWFzaSBwYXJhbWV0ZXIsIGRhbiBrZW11ZGlhbiBtZW5nZ3VuYWthbiBtb2RlbC1tb2RlbCB5YW5nIGNvY29rIGRlbmdhbiBkYXRhIHlhbmcgZGl0YWhhbiB1bnR1ayBtZW5lbnR1a2FuIHN0YXRpc3RpayBLb2xtb2dvcm92LVNtaXJub3YuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShWR0FNKQ0KbGlicmFyeShNQVNTKQ0KY2xhaW1fbGV2IDwtIHJlYWQuY3N2KCJDTEFJTUxFVkVMLmNzdiIsIGhlYWRlciA9IFRSVUUpIA0KY2xhaW1fZGF0YSA8LSBzdWJzZXQoY2xhaW1fbGV2LCBZZWFyID09IDIwMTApOyANCg0KIyBSYW5kb21seSByZS1vcmRlciB0aGUgZGF0YSAtICJzaHVmZmxlIGl0Ig0KbiA8LSBucm93KGNsYWltX2RhdGEpDQpzZXQuc2VlZCgxMjM0NykNCmN2ZGF0YSA8LSBjbGFpbV9kYXRhW3NhbXBsZShuKSwgXQ0KIyBOdW1iZXIgb2YgZm9sZHMNCmsgPC0gOA0KY3ZhbHZlYyA8LSBtYXRyaXgoMCwyLGspDQpmb3IgKGkgaW4gMTprKSB7DQogIGluZGljZXMgPC0gKCgoaS0xKSAqIHJvdW5kKCgxL2spKm5yb3coY3ZkYXRhKSkpICsgMSk6KChpKnJvdW5kKCgxL2spICogbnJvdyhjdmRhdGEpKSkpDQojIFBhcmV0bw0KICBmaXQucGFyZXRvIDwtIHZnbG0oQ2xhaW0gfiAxLCBwYXJldG9JSSwgbG9jID0gMCwgZGF0YSA9IGN2ZGF0YVstaW5kaWNlcyxdKQ0KICBrc1Jlc3VsdFBhcmV0byA8LSBrcy50ZXN0KGN2ZGF0YVtpbmRpY2VzLF0kQ2xhaW0sICJwcGFyZXRvSUkiLCBsb2MgPSAwLCBzaGFwZSA9IGV4cChjb2VmKGZpdC5wYXJldG8pWzJdKSwgDQogICAgICAgIHNjYWxlID0gZXhwKGNvZWYoZml0LnBhcmV0bylbMV0pKQ0KICBjdmFsdmVjWzEsaV0gPC0ga3NSZXN1bHRQYXJldG8kc3RhdGlzdGljDQojIEdhbW1hDQogIGZpdC5nYW1tYSA8LSBnbG0oQ2xhaW0gfiAxLCBkYXRhID0gY3ZkYXRhWy1pbmRpY2VzLF0sIGZhbWlseSA9IEdhbW1hKGxpbmsgPSBsb2cpKSANCiAgZ2FtbWFfdGhldGEgPC0gZXhwKGNvZWYoZml0LmdhbW1hKSkgKiBnYW1tYS5kaXNwZXJzaW9uKGZpdC5nYW1tYSkgIA0KICBhbHBoYSA8LSAxIC8gZ2FtbWEuZGlzcGVyc2lvbihmaXQuZ2FtbWEpDQogIGtzUmVzdWx0R2FtbWEgPC0ga3MudGVzdChjdmRhdGFbaW5kaWNlcyxdJENsYWltLCAicGdhbW1hIiwgc2hhcGUgPSBhbHBoYSwgc2NhbGUgPSBnYW1tYV90aGV0YSkNCiAgY3ZhbHZlY1syLGldIDwtIGtzUmVzdWx0R2FtbWEkc3RhdGlzdGljDQp9DQpLU2N2IDwtIHJvd1N1bXMoY3ZhbHZlYykvaw0KYGBgDQoNCkhhc2lsbnlhIHRhbXBhayBwYWRhIEdhbWJhciA2LjEyIGRpIG1hbmEgc3VtYnUgaG9yaXpvbnRhbCBhZGFsYWggRm9sZD0xLiBQcm9zZXMgaW5pIGRpdWxhbmdpIHVudHVrIHR1anVoIGxpcGF0YW4gbGFpbm55YS4gSGFzaWwgeWFuZyBkaXJhbmdrdW0gZGFsYW0gR2FtYmFyIDYuMTIgbWVudW5qdWtrYW4gYmFod2EgUGFyZXRvIHNlY2FyYSBrb25zaXN0ZW4gbWVtYmVyaWthbiBkaXN0cmlidXNpIHByZWRpa3RpZiB5YW5nIGxlYmloIGRhcGF0IGRpYW5kYWxrYW4gZGFyaXBhZGEgZ2FtbWEuDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBzdGF0aXN0aWNzDQptYXRwbG90KDE6ayx0KGN2YWx2ZWMpLHR5cGU9ImIiLCBjb2w9YygxLDMpLCBsdHk9MToyLCANCiAgICAgICAgeWxpbT1jKDAsMC40KSwgcGNoID0gMCwgeGxhYj0iRm9sZCIsIHlsYWI9IktTIFN0YXRpc3RpYyIpDQpsZWdlbmQoImxlZnQiLCBjKCJQYXJldG8iLCAiR2FtbWEiKSwgY29sPWMoMSwzKSxsdHk9MToyLCBidHk9Im4iKQ0KYGBgDQo8cCBjbGFzcz0iY2FwdGlvbiI+DQogICJGaWd1cmUgNi4yOiAiDQo8c3Ryb25nPiANCiAgU3RhdGlzdGlrIEtvbG1vZ29yb3YtU21pcm5vdiAoS1MpIHlhbmcgdGVsYWggZGl2YWxpZGFzaSBzaWxhbmcgdW50dWsgRGF0YSBLbGFpbSBEYW5hIEFzdXJhbnNpLiANCjwvc3Ryb25nPg0KICBHYXJpcyBoaXRhbSBzb2xpZCB1bnR1ayBkaXN0cmlidXNpIFBhcmV0bywgZ2FyaXMgcHV0dXMtcHV0dXMgaGlqYXUgdW50dWsgZGlzdHJpYnVzaSBnYW1tYS4gU3RhdGlzdGlrIEtTIG1lbmd1a3VyIGRldmlhc2kgdGVyYmVzYXIgYW50YXJhIGRpc3RyaWJ1c2kgeWFuZyBzZXN1YWkgZGVuZ2FuIGRpc3RyaWJ1c2kgZW1waXJpcyB1bnR1ayBtYXNpbmctbWFzaW5nIGRhcmkgOCBrZWxvbXBvaywgYXRhdSBsaXBhdGFuLCBkYXRhIHlhbmcgZGlwaWxpaCBzZWNhcmEgYWNhay4gDQo8L3A+DQoNCiMjIDYuMy4yIExlYXZlLU9uZS1PdXQgQ3Jvc3MtVmFsaWRhdGlvbg0KDQoNCkthc3VzIGtodXN1cyBkaSBtYW5hICRrPW4kIGRpa2VuYWwgc2ViYWdhaSB2YWxpZGFzaSBzaWxhbmcgdGluZ2dhbGthbi1zYXR1LWtlbHVhci4gS2FzdXMgaW5pIHNlY2FyYSBoaXN0b3JpcyBzYW5nYXQgbWVub25qb2wgZGFuIHRlcmthaXQgZXJhdCBkZW5nYW4gamFja2tuaWZlc3RhdGlzdGlrIHlhbmcgbWVydXBha2FuIHBlbmRhaHVsdSBkYXJpIHRla25payBib290c3RyYXAuDQoNCk1lc2tpcHVuIGtpdGEgbWVueWFqaWthbm55YSBzZWJhZ2FpIGthc3VzIGtodXN1cyB2YWxpZGFzaSBzaWxhbmcsIGFrYW4gc2FuZ2F0IG1lbWJhbnR1IGppa2Ega2FtaSBtZW1iZXJpa2FuIGRlZmluaXNpIGVrc3BsaXNpdC4gUGVydGltYmFuZ2thbiBzZWJ1YWggc3RhdGlzdGlrIHVtdW0gJM64y4YgPSB0KHgpJCB5YW5nIG1lcnVwYWthbiBwZW5ha3NpciB1bnR1ayBzZWJ1YWggcGFyYW1ldGVyIHlhbmcgZGltaW5hdGkgJM64JC4gSWRlIGRhcmkgamFja2tuaWZlIGFkYWxhaCBtZW5naGl0dW5nIG4gbmlsYWkgJM64y4Zfey1pfSA9IHQoeC1pKSQsIGRpIG1hbmEgJHgtaSQgYWRhbGFoIHN1YnNhbXBlbCBkYXJpICR4JCBkZW5nYW4gbmlsYWkgJGtlLWkkIGRpaGlsYW5na2FuLiBSYXRhLXJhdGEgZGFyaSBuaWxhaS1uaWxhaSBpbmkgZGlsYW1iYW5na2FuIHNlYmFnYWkNCg0KJCRcb3ZlcmxpbmV7XHdpZGVoYXR7XHRoZXRhfX1feyhcY2RvdCl9PVxmcmFjezF9e259XHN1bV97aT0xfV5uIFx3aWRlaGF0e1x0aGV0YX1fey1pfSAuJCQNCg0KTmlsYWktbmlsYWkgaW5pIGRhcGF0IGRpZ3VuYWthbiB1bnR1ayBtZW1idWF0IGVzdGltYXNpIGJpYXMgZGFyaSBzdGF0aXN0aWsgJFxoYXTOuCQNCg0KJCRcYmVnaW57ZXF1YXRpb259DQpCaWFzX3tqYWNrfSA9IChuLTEpIFxsZWZ0KFxvdmVybGluZXtcd2lkZWhhdHtcdGhldGF9fV97KFxjZG90KX0gLSBcd2lkZWhhdHtcdGhldGF9XHJpZ2h0KQ0KXHRhZ3s2LjN9DQpcZW5ke2VxdWF0aW9ufSQkDQoNCnNlcnRhIGVzdGltYXNpIHN0YW5kYXIgZGV2aWFzaQ0KDQokJFxiZWdpbntlcXVhdGlvbn0NCnNfe2phY2t9ID1cc3FydHtcZnJhY3tuLTF9e259XHN1bV97aT0xfV5uIFxsZWZ0KFx3aWRlaGF0e1x0aGV0YX1fey1pfSAtXG92ZXJsaW5le1x3aWRlaGF0e1x0aGV0YX19X3soXGNkb3QpfVxyaWdodCleMn0gfi4NClx0YWd7Ni40fQ0KXGVuZHtlcXVhdGlvbn0kJA0KDQoqKkNvbnRvaCA2LjMuMi4gS29lZmlzaWVuIFZhcmlhc2kuKiogU2ViYWdhaSBpbHVzdHJhc2ksIHBlcnRpbWJhbmdrYW4gc2VidWFoIHNhbXBlbCBmaWt0aWYga2VjaWwgJHggPSB7eF8xLC4uLix4X259JCBkZW5nYW4gcmVhbGlzYXNpDQoNCmBgYHtyfQ0Kc2FtcGxlX3ggPC0gYygyLjQ2LDIuODAsMy4yOCwzLjg2LDIuODUsMy42NywzLjM3LDMuNDAsDQogICAgICAgICAgICAgIDUuMjIsMi41NSwyLjc5LDQuNTAsMy4zNywyLjg4LDEuNDQsMi41NiwyLjAwLDIuMDcsMi4xOSwxLjc3KQ0KYGBgDQoNCk1pc2Fsa2FuIGtpdGEgdGVydGFyaWsgZGVuZ2FuICRcdGhldGEgPSBDViA9IFxzcXJ0e1xtYXRocm17VmFyfn1bWF19L1xtYXRocm17RX59W1hdJA0KDQpEZW5nYW4gZGF0YXNldCBpbmksIGVzdGltYXRvciBrb2VmaXNpZW4gdmFyaWFzaSBtZW5qYWRpIDAsMzExOTYuIE5hbXVuLCBzZWJlcmFwYSBoYW5kYWxrYWggZXN0aW1hc2kgdGVyc2VidXQ/IFVudHVrIG1lbmphd2FiIHBlcnRhbnlhYW4gaW5pLCBraXRhIGRhcGF0IG1lbmdoaXR1bmcgZXN0aW1hdG9yIHBpc2F1IGxpcGF0IGRhcmkgYmlhcyBkYW4gZGV2aWFzaSBzdGFuZGFybnlhLiBLb2RlIGJlcmlrdXQgaW5pIG1lbnVuanVra2FuIGJhaHdhIHBlbmFrc2lyIGphY2trbmlmZSB1bnR1ayBiaWFzIGFkYWxhaCAkQmlhc197amFja30gPSAtMCwwMDYyNyQgZGFuIHN0YW5kYXIgZGV2aWFzaSBqYWNra25pZmUgYWRhbGFoICRzX3tqYWNrfSA9IDAsMDEyOTMkLg0KDQpgYGB7cn0NCkNWYXIgPC0gZnVuY3Rpb24oeCkgc3FydCh2YXIoeCkpL21lYW4oeCkNCkphY2tDVmFyIDwtIGZ1bmN0aW9uKGkpIHNxcnQodmFyKHNhbXBsZV94Wy1pXSkpL21lYW4oc2FtcGxlX3hbLWldKQ0KSmFja1RoZXRhIDwtIFZlY3Rvcml6ZShKYWNrQ1ZhcikoMTpsZW5ndGgoc2FtcGxlX3gpKQ0KQmlhc0phY2sgPC0gKGxlbmd0aChzYW1wbGVfeCktMSkqKG1lYW4oSmFja1RoZXRhKSAtIENWYXIoc2FtcGxlX3gpKQ0Kc2QoSmFja1RoZXRhKQ0KYGBgDQoNCioqQ29udG9oIDYuMy4zLiBLbGFpbSBDaWRlcmEgQmFkYW4gZGFuIFJhc2lvIEVsaW1pbmFzaSBLZXJ1Z2lhbi4qKiBQYWRhIENvbnRvaCA2LjIuMSwga2l0YSB0ZWxhaCBtZW51bmp1a2thbiBiYWdhaW1hbmEgbWVuZ2hpdHVuZyBlc3RpbWFzaSBib290c3RyYXAgZGFyaSBiaWFzIGRhbiBkZXZpYXNpIHN0YW5kYXIgdW50dWsgcmFzaW8gZWxpbWluYXNpIGtlcnVnaWFuIGRlbmdhbiBtZW5nZ3VuYWthbiBkYXRhIGtsYWltIGNlZGVyYSBiYWRhbiBwYWRhIENvbnRvaCA0LjEuMTEuIFNla2FyYW5nIGtpdGEgbWVuaW5kYWtsYW5qdXRpIGRlbmdhbiBtZW1iZXJpa2FuIGp1bWxhaCB5YW5nIHNlYmFuZGluZyBkZW5nYW4gbWVuZ2d1bmFrYW4gc3RhdGlzdGlrIGphY2trbmlmZS4NCg0KVGFiZWwgNi43IG1lcmFuZ2t1bSBoYXNpbCBlc3RpbWFzaSBqYWNra25pZmUuIFRhYmVsIGluaSBtZW51bmp1a2thbiBiYWh3YSBlc3RpbWFzaSBqYWNra25pZmUgdGVyaGFkYXAgYmlhcyBkYW4gZGV2aWFzaSBzdGFuZGFyIGRhcmkgcmFzaW8gZWxpbWluYXNpIGtlcnVnaWFuICRFIFttaW4gKFgsIGQpXS9FIFtYXSQgc2ViYWdpYW4gYmVzYXIga29uc2lzdGVuIGRlbmdhbiBtZXRvZG9sb2dpIGJvb3RzdHJhcC4gU2VsYWluIGl0dSwga2l0YSBkYXBhdCBtZW5nZ3VuYWthbiBzdGFuZGFyIGRldmlhc2kgdW50dWsgbWVtYmFuZ3VuIGludGVydmFsIGtlcGVyY2F5YWFuIGJlcmJhc2lzIG5vcm1hbCwgeWFuZyBiZXJwdXNhdCBkaSBzZWtpdGFyIHBlbmFrc2lyIHlhbmcgZGlrb3Jla3NpIGJpYXMuIFNlYmFnYWkgY29udG9oLCBwYWRhICRkID0gMTQwMDAkLCBraXRhIG1lbGloYXQgcGFkYSBDb250b2ggNC4xLjExIGJhaHdhIGVzdGltYXNpIG5vbnBhcmFtZXRyaWsgZGFyaSAkTEVSJCBhZGFsYWggMC45NzY3OC4gRXN0aW1hc2kgaW5pIG1lbWlsaWtpIGJpYXMgc2ViZXNhciAwLDAwMDEwLCBzZWhpbmdnYSBtZW5naGFzaWxrYW4gZXN0aW1hdG9yIHRlcmtvcmVrc2ktYmlhcyBzZWJlc2FyIDAsOTc2ODguIEludGVydmFsIGtlcGVyY2F5YWFuIDk1JSBkaWhhc2lsa2FuIGRlbmdhbiBtZW1idWF0IGludGVydmFsIGR1YSBrYWxpIHBhbmphbmcgMSw5NiBkZXZpYXNpIHN0YW5kYXIgamFja2tuaWZlLCB5YW5nIGJlcnB1c2F0IHBhZGEgZXN0aW1hdG9yIHRlcmtvcmVrc2kgYmlhcyAoMSw5NiBhZGFsYWggcGVya2lyYWFuIGt1YW50aWwga2UtOTcsNSBkYXJpIGRpc3RyaWJ1c2kgbm9ybWFsIHN0YW5kYXIpLg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoYm9vdCkNCiMgRXhhbXBsZSBmcm9tIERlcnJpZyBldCBhbA0KQklEYXRhIDwtIHJlYWQuY3N2KCJEZXJyaWdSZXNhbXBsaW5nLmNzdiIsIGhlYWRlciA9VCkNCkJJRGF0YSRDZW5zb3JlZCA8LSAxKihCSURhdGEkQW1vdW50UGFpZCA+PSBCSURhdGEkUG9saWN5TGltaXQpDQpCSURhdGFVbmNlbnNvcmVkIDwtIHN1YnNldChCSURhdGEsIENlbnNvcmVkID09IDApDQpMRVIuYm9vdCA8LSBmdW5jdGlvbihkZWQsIGRhdGEsIGluZGljZXMpew0KICByZXNhbXBsZS5kYXRhIDwtIGRhdGFbaW5kaWNlcyxdDQogIHN1bUNsYWltcyA8LSBzdW0ocmVzYW1wbGUuZGF0YSRBbW91bnRQYWlkKQ0KICBzdW1DbGFpbXNfZCA8LSBzdW0ocG1pbihyZXNhbXBsZS5kYXRhJEFtb3VudFBhaWQsZGVkKSkNCiAgTEVSIDwtICAgc3VtQ2xhaW1zX2Qvc3VtQ2xhaW1zDQogIHJldHVybihMRVIpICANCn0NCg0KeCA8LSBCSURhdGFVbmNlbnNvcmVkJEFtb3VudFBhaWQNCkxFUi5qYWNrPC0gZnVuY3Rpb24oZGVkLGkpew0KICBMRVIgPC0gICBzdW0ocG1pbih4Wy1pXSxkZWQpKS9zdW0oeFstaV0pDQogIHJldHVybihMRVIpICANCn0NCkxFUiA8LSBmdW5jdGlvbihkZWQpIHN1bShwbWluKHgsZGVkKSkvc3VtKHgpDQojI0RlcnJpZyBldCBhbA0Kc2V0LnNlZWQoMjAxOSkNCmRWZWMyIDwtIGMoNDAwMCwgNTAwMCwgMTA1MDAsIDExNTAwLCAxNDAwMCwgMTg1MDApDQpPdXRKYWNrIDwtIG1hdHJpeCgwLGxlbmd0aChkVmVjMiksOCkNCiAgZm9yIChqIGluIDE6bGVuZ3RoKGRWZWMyKSkgew0KT3V0SmFja1tqLDFdIDwtIGRWZWMyW2pdDQpyZXN1bHRzIDwtIGJvb3QoZGF0YT1CSURhdGFVbmNlbnNvcmVkLCBzdGF0aXN0aWM9TEVSLmJvb3QsIFI9MTAwMCwgZGVkPWRWZWMyW2pdKQ0KT3V0SmFja1tqLDJdIDwtIHJlc3VsdHMkdDANCmJpYXNib290IDwtIG1lYW4ocmVzdWx0cyR0KS1yZXN1bHRzJHQwIC0+IE91dEphY2tbaiwzXQ0Kc2Rib290IDwtIHNkKHJlc3VsdHMkdCkgLT4gT3V0SmFja1tqLDRdDQp0ZW1wIDwtIGJvb3QuY2kocmVzdWx0cykNCg0KTEVSLmphY2suZGVkPC0gZnVuY3Rpb24oaSkgTEVSLmphY2soZGVkPWRWZWMyW2pdLGkpDQpKYWNrVGhldGEuZGVkIDwtIFZlY3Rvcml6ZShMRVIuamFjay5kZWQpKDE6bGVuZ3RoKHgpKQ0KT3V0SmFja1tqLDVdIDwtIEJpYXNKYWNrLmRlZCA8LSAobGVuZ3RoKHgpLTEpKihtZWFuKEphY2tUaGV0YS5kZWQpIC0gTEVSKGRlZD1kVmVjMltqXSkpDQpPdXRKYWNrW2osNl0gPC0gc2QoSmFja1RoZXRhLmRlZCkNCk91dEphY2tbaiw3OjhdIDwtIG1lYW4oSmFja1RoZXRhLmRlZCkrcXQoYygwLjAyNSwwLjk3NSksbGVuZ3RoKHgpLTEpKk91dEphY2tbaiw2XQ0KICB9DQpgYGANCg0KW1RhYmxlIDYuN106XCN0YWI6NjcNCg0KPGEgaWQ9dGFiOjY3PjwvYT4NCg0KVGFibGUgNi43LiAqKkVzdGltYXNpIEphY2trbmlmZSBkYXJpIExFUiBwYWRhIERlZHVjdGlibGUgeWFuZyBEaXBpbGloKioNCg0KYGBge3IgY29tbWVudD0iIiwgZWNobz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpPdXRKYWNrLmxhdGV4IDwtIE91dEphY2sNCmNvbG5hbWVzKE91dEphY2spIDwtIGMoImQiLCJOUCBFc3RpbWF0ZSIsIkJvb3RzdHJhcCBCaWFzIiwgIkJvb3RzdHJhcCBTRCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAiSmFja2tuaWZlIEJpYXMiLCAiSmFja2tuaWZlIFNEIiwiTG93ZXIgSmFja2tuaWZlIDk1JSBDSSIsICJVcHBlciBKYWNra25pZmUgOTUlIENJIikNCmlmIChrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSkge2tuaXRyOjprYWJsZShPdXRKYWNrLCAiaHRtbCIsZGlnaXRzPTUpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLA0KICAgICAgICAgIGZvbnRfc2l6ZSA9IDEwKQ0KfQ0KaWYgKGtuaXRyOjppc19sYXRleF9vdXRwdXQoKSkge2tuaXRyOjprYWJsZShPdXRKYWNrLmxhdGV4LCAibGF0ZXgiLCBib29rdGFicyA9IFQsIGRpZ2l0cz01KSAlPiUNCiAga2FibGVfc3R5bGluZyhsYXRleF9vcHRpb25zPSJzY2FsZV9kb3duIikgJT4lDQogICAgYWRkX2hlYWRlcl9hYm92ZShjKCIiLCJFc3RpbWF0ZSIsIkJpYXMiLCAiU0QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIkJpYXMiLCAiU0QiLCI5NSUgQ0kiLCAiOTUlIENJIikpICU+JQ0KICAgICAgYWRkX2hlYWRlcl9hYm92ZShjKCJkIiwiTlAiLCJCb290c3RyYXAiLCAiQm9vdHN0cmFwIiwgDQogICAgICAgICAgICAgICAgICAgICAgICJKYWNra25pZmUiLCAiSmFja2tuaWZlIiwiTG93ZXIgSmFja2tuaWZlIiwgIlVwcGVyIEphY2trbmlmZSIpKQ0KfQ0KYGBgDQoNCkRpc2t1c2kuIFNhbGFoIHNhdHUgZGFyaSBiYW55YWsgaGFsIG1lbmFyaWsgdGVudGFuZyBrYXN1cyBraHVzdXMgbGVhdmUtb25lLW91dCBhZGFsYWgga2VtYW1wdWFuIHVudHVrIG1lcmVwbGlrYXNpIGVzdGltYXNpIGRlbmdhbiB0ZXBhdC4gQXJ0aW55YSwga2V0aWthIHVrdXJhbiBsaXBhdGFuIGhhbnlhIHNhdHUsIG1ha2EgdGlkYWsgYWRhIGtldGlkYWtwYXN0aWFuIHRhbWJhaGFuIHlhbmcgZGlzZWJhYmthbiBvbGVoIHZhbGlkYXNpIHNpbGFuZy4gSW5pIGJlcmFydGkgYmFod2EgcGFyYSBhbmFsaXMgZGFwYXQgbWVyZXBsaWthc2kgcGVrZXJqYWFuIHNhdHUgc2FtYSBsYWluIGRlbmdhbiB0ZXBhdCwgc2VidWFoIHBlcnRpbWJhbmdhbiB5YW5nIHBlbnRpbmcuDQoNClN0YXRpc3RpayBKYWNra25pZmUgZGlrZW1iYW5na2FuIHVudHVrIG1lbWFoYW1pIGtldGVwYXRhbiBlc3RpbWF0b3IsIG1lbmdoYXNpbGthbiBlc3RpbWF0b3IgYmlhcyBkYW4gZGV2aWFzaSBzdGFuZGFyIHBhZGEgcGVyc2FtYWFuICg2LjMpIGRhbiAoNi40KS4gSGFsIGluaSBzZXN1YWkgZGVuZ2FuIHR1anVhbiB5YW5nIHRlbGFoIGtpdGEga2FpdGthbiBkZW5nYW4gdGVrbmlrIGJvb3RzdHJhcCwgYnVrYW4gbWV0b2RlIHZhbGlkYXNpIHNpbGFuZy4gSGFsIGluaSBtZW51bmp1a2thbiBiYWdhaW1hbmEgdGVrbmlrIHN0YXRpc3RpayBkYXBhdCBkaWd1bmFrYW4gdW50dWsgbWVuY2FwYWkgdHVqdWFuIHlhbmcgYmVyYmVkYS4NCg0KIyMgNi4zLjMgQ3Jvc3MtVmFsaWRhdGlvbiBhbmQgQm9vdHN0cmFwDQoNCkJvb3RzdHJhcCBiZXJndW5hIHVudHVrIG1lbWJlcmlrYW4gZXN0aW1hdG9yIHByZXNpc2ksIGF0YXUgdmFyaWFiaWxpdGFzLCBkYXJpIHN0YXRpc3Rpay4gSGFsIGluaSBqdWdhIGJlcmd1bmEgdW50dWsgdmFsaWRhc2kgbW9kZWwuIFBlbmRla2F0YW4gYm9vdHN0cmFwIHVudHVrIHZhbGlkYXNpIG1vZGVsIG1pcmlwIGRlbmdhbiBwcm9zZWR1ciB2YWxpZGFzaSBsZWF2ZS1vbmUtb3V0IGRhbiAqayotZm9sZDoNCg0KKiBCdWF0IHNhbXBlbCBib290c3RyYXAgZGVuZ2FuIG1lbmdhbWJpbCBzYW1wZWwgdWxhbmcgKGRlbmdhbiBwZW5nZ2FudGlhbikgJG4kIGluZGVrcyBkYWxhbSAkezEsIOKLrywgbn0kLiBJbmkgYWthbiBtZW5qYWRpIHNhbXBlbCBwZWxhdGloYW4ga2l0YS4gUGVya2lyYWthbiBtb2RlbCB5YW5nIHNlZGFuZyBkaXBlcnRpbWJhbmdrYW4gYmVyZGFzYXJrYW4gc2FtcGVsIGluaS4NCg0KKiAqVWppKiwgYXRhdSAqc2FtcGVsIHZhbGlkYXNpKiwgdGVyZGlyaSBkYXJpIHBlbmdhbWF0YW4geWFuZyB0aWRhayBkaXBpbGloIHVudHVrIHBlbGF0aWhhbi4gTWVuZ2V2YWx1YXNpIG1vZGVsIHlhbmcgY29jb2sgKGJlcmRhc2Fya2FuIGRhdGEgcGVsYXRpaGFuKSBkZW5nYW4gbWVuZ2d1bmFrYW4gZGF0YSB1amkuDQoNClVsYW5naSBwcm9zZXMgaW5pIGJlYmVyYXBhIGthbGkgKGthdGFrYW5sYWggJEIkKS4gQW1iaWwgcmF0YS1yYXRhIGRhcmkgaGFzaWwtaGFzaWxueWEgZGFuIHBpbGloIG1vZGVsIGJlcmRhc2Fya2FuIHN0YXRpc3RpayBldmFsdWFzaSByYXRhLXJhdGEuDQoNCioqQ29udG9oIDYuMy40LiBEYW5hIFByb3BlcnRpIFdpc2NvbnNpbioqLiBLZW1iYWxpIGtlIENvbnRvaCA2LjMuMSBkaSBtYW5hIGtpdGEgbWVueWVsaWRpa2kga2Vjb2Nva2FuIGRpc3RyaWJ1c2kgZ2FtbWEgZGFuIFBhcmV0byBwYWRhIGRhdGEgZGFuYSBwcm9wZXJ0aS4gS2l0YSBrZW1iYWxpIG1lbWJhbmRpbmdrYW4ga2luZXJqYSBwcmVkaWtzaSBtZW5nZ3VuYWthbiBzdGF0aXN0aWsgS29sbW9nb3Jvdi1TbWlybm92IChLUyksIG5hbXVuIGthbGkgaW5pIG1lbmdndW5ha2FuIHByb3NlZHVyIGJvb3RzdHJhcCB1bnR1ayBtZW1iYWdpIGRhdGEgYW50YXJhIHNhbXBlbCBwZWxhdGloYW4gZGFuIHBlbmd1amlhbi4gQmVyaWt1dCBpbmkgYWRhbGFoIGtvZGUgaWx1c3RyYXNpbnlhLg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ29mdGVzdCkNCm4gPC0gbnJvdyhjbGFpbV9kYXRhKQ0Kc2V0LnNlZWQoMTIzNDcpDQppbmRpY2VzIDwtIDE6bg0KIyBOdW1iZXIgb2YgQm9vdHN0cmFwIFNhbXBsZXMNCkIgPC0gMTAwDQpjdmFsdmVjIDwtIG1hdHJpeCgwLDIsQikNCmZvciAoaSBpbiAxOkIpIHsNCiAgYm9vdGluZGV4IDwtIHVuaXF1ZShzYW1wbGUoaW5kaWNlcywgc2l6ZT1uLCByZXBsYWNlPSBUUlVFKSkNCiAgdHJhaW5kYXRhIDwtIGNsYWltX2RhdGFbYm9vdGluZGV4LF0NCiAgdGVzdGRhdGEgIDwtIGNsYWltX2RhdGFbLWJvb3RpbmRleCxdDQojIFBhcmV0bw0KICBmaXQucGFyZXRvIDwtIHZnbG0oQ2xhaW0gfiAxLCBwYXJldG9JSSwgbG9jID0gMCwgZGF0YSA9IHRyYWluZGF0YSkNCiAga3NSZXN1bHRQYXJldG8gPC0ga3MudGVzdCh0ZXN0ZGF0YSRDbGFpbSwgInBwYXJldG9JSSIsIGxvYyA9IDAsIHNoYXBlID0gZXhwKGNvZWYoZml0LnBhcmV0bylbMl0pLCANCiAgICAgICAgc2NhbGUgPSBleHAoY29lZihmaXQucGFyZXRvKVsxXSkpDQogIGN2YWx2ZWNbMSxpXSA8LSBrc1Jlc3VsdFBhcmV0byRzdGF0aXN0aWMNCiMgR2FtbWENCiAgZml0LmdhbW1hIDwtIGdsbShDbGFpbSB+IDEsIGRhdGEgPSB0cmFpbmRhdGEsIGZhbWlseSA9IEdhbW1hKGxpbmsgPSBsb2cpKSANCiAgZ2FtbWFfdGhldGEgPC0gZXhwKGNvZWYoZml0LmdhbW1hKSkgKiBnYW1tYS5kaXNwZXJzaW9uKGZpdC5nYW1tYSkgIA0KICBhbHBoYSA8LSAxIC8gZ2FtbWEuZGlzcGVyc2lvbihmaXQuZ2FtbWEpDQogIGtzUmVzdWx0R2FtbWEgPC0ga3MudGVzdCh0ZXN0ZGF0YSRDbGFpbSwgInBnYW1tYSIsIHNoYXBlID0gYWxwaGEsIHNjYWxlID0gZ2FtbWFfdGhldGEpDQogIGN2YWx2ZWNbMixpXSA8LSBrc1Jlc3VsdEdhbW1hJHN0YXRpc3RpYw0KfQ0KS1NCb290IDwtIHJvd1N1bXMoY3ZhbHZlYykvQg0KYGBgDQoNCkthbWkgbWVsYWt1a2FuIHBlbmdhbWJpbGFuIHNhbXBlbCBkZW5nYW4gbWVuZ2d1bmFrYW4gQj0gMTAwIHVsYW5nYW4uIFN0YXRpc3RpayBLUyByYXRhLXJhdGEgdW50dWsgZGlzdHJpYnVzaSBQYXJldG8gYWRhbGFoIDAsMDU4IGRpYmFuZGluZ2thbiBkZW5nYW4gcmF0YS1yYXRhIHVudHVrIGRpc3RyaWJ1c2kgZ2FtbWEsIDAsMjYyLiBIYWwgaW5pIGtvbnNpc3RlbiBkZW5nYW4gaGFzaWwgc2ViZWx1bW55YSBkYW4gbWVtYmVyaWthbiBidWt0aSBsYWluIGJhaHdhIFBhcmV0byBhZGFsYWggbW9kZWwgeWFuZyBsZWJpaCBiYWlrIHVudHVrIGRhdGEgaW5pIGRpYmFuZGluZ2thbiBkZW5nYW4gZ2FtbWEuDQoNCiMgNi40IEltcG9ydGFuY2UgU2FtcGxpbmcNCg0KQmFnaWFuIDYuMSBtZW1wZXJrZW5hbGthbiB0ZWtuaWsgTW9udGUgQ2FybG8gZGVuZ2FuIG1lbmdndW5ha2FuIHRla25payBpbnZlcnNpOiB1bnR1ayBtZW1iYW5na2l0a2FuIHNlYnVhaCB2YXJpYWJlbCBhY2FrICRYJCBkZW5nYW4gZGlzdHJpYnVzaSAkRiQsIHRlcmFwa2FuICRGXnstMX0kIHBhZGEgcGVtYW5nZ2lsYW4gc2VidWFoIGdlbmVyYXRvciBhY2FrIChzZXJhZ2FtIHBhZGEgaW50ZXJ2YWwgc2F0dWFuKS4gQmFnYWltYW5hIGppa2Ega2l0YSBpbmdpbiBtZW5nZ2FtYmFyIHNlc3VhaSBkZW5nYW4gJFgkLCBkZW5nYW4gc3lhcmF0ICRY4oiIW2EsYl0kPw0KDQpTZXNlb3JhbmcgZGFwYXQgbWVuZ2d1bmFrYW4gbWVrYW5pc21lIHRlcmltYS10b2xhazogbWVuYXJpayAkeCQgZGFyaSBkaXN0cmlidXNpICRGJA0KDQoqIGppa2EgJHhcaW5bYSxiXSQ6IHNpbXBhbiAoInRlcmltYSIpDQoNCiogamlrYSAkeFxub3RpblthLGJdJDogZ2FtYmFyIHlhbmcgbGFpbiAoInRvbGFrIikNCg0KQW1hdGkgYmFod2EgZGFyaSBuIG5pbGFpIHlhbmcgYXdhbG55YSBkaWhhc2lsa2FuLCBraXRhIHNpbXBhbiBkaSBzaW5pIGhhbnlhICRbRihiKS1GKGEpXSDii4UgbiQgaGFzaWwgaW1iYW5nLCByYXRhLXJhdGEuDQoNCioqQ29udG9oIDYuNC4xLiBQZW5hcmlrYW4gZGFyaSBEaXN0cmlidXNpIE5vcm1hbC4qKiBNaXNhbGthbiBraXRhIG1lbmdnYW1iYXIgZGFyaSBkaXN0cmlidXNpIG5vcm1hbCBkZW5nYW4gcmF0YS1yYXRhIDIsNSBkYW4gdmFyaWFucyAxLCAkTigyLDUsMSkkLCB0ZXRhcGkgaGFueWEgdGVydGFyaWsgcGFkYSBnYW1iYXIgeWFuZyBsZWJpaCBiZXNhciBkYXJpICRh4omlMiQgZGFuIGt1cmFuZyBkYXJpICRi4omkNCQuIEFydGlueWEsIGtpdGEgaGFueWEgZGFwYXQgbWVuZ2d1bmFrYW4gJEYoNCktRigyKT3Opig0LTIuNSktzqYoMi0yLjUpID0gMC45MzMyIC0gMC4zMDg1ID0gMC42MjQ3JCBwcm9wb3JzaSB1bmRpYW4uIEdhbWJhciA2LjEzIG1lbnVuanVra2FuIGJhaHdhIGJlYmVyYXBhIGhhc2lsIHVuZGlhbiBiZXJhZGEgZGkgZGFsYW0gaW50ZXJ2YWwgJCgyLDQpJCBkYW4gYmViZXJhcGEgZGkgbHVhcm55YS4NCg0KYGBge3J9DQptdSA9IDIuNQ0Kc2lnbWEgPSAxDQphID0gMg0KYiA9IDQNCkZhID0gcG5vcm0oYSxtdSxzaWdtYSkNCkZiID0gcG5vcm0oYixtdSxzaWdtYSkNCnBpY19hbmkgPSBmdW5jdGlvbigpew0KICB1PXNlcSgwLDUsYnk9LjAxKQ0KICBwbG90KHUscG5vcm0odSxtdSxzaWdtYSksY29sPSJ3aGl0ZSIseWxhYj0iIix4bGFiPSIiKQ0KICByZWN0KC0xLC0xLDYsMixjb2w9cmdiKDEsMCwwLC4yKSxib3JkZXI9TkEpDQogIHJlY3QoYSxGYSxiLEZiLGNvbD0id2hpdGUiLGJvcmRlcj1OQSkNCiAgbGluZXModSxwbm9ybSh1LG11LHNpZ21hKSxsd2Q9MikNCiAgYWJsaW5lKHY9YyhhLGIpLGx0eT0yLGNvbD0icmVkIikNCiAgcnUgPC0gcnVuaWYoMSkNCiAgY2xyIDwtICJyZWQiDQogIGlmKChxbm9ybShydSxtdSxzaWdtYSk+PWEpJihxbm9ybShydSxtdSxzaWdtYSk8PWIpKSBjbHIgPC0gImJsdWUiDQogIHNlZ21lbnRzKC0xLHJ1LHFub3JtKHJ1LG11LHNpZ21hKSxydSxjb2w9Y2xyLGx3ZD0yKQ0KICBhcnJvd3MocW5vcm0ocnUsbXUsc2lnbWEpLHJ1LHFub3JtKHJ1LG11LHNpZ21hKSwwLGNvbD1jbHIsbHdkPTIsbGVuZ3RoID0gLjEpDQp9DQpgYGANCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCmZvciAoaSBpbiAxOm51bUFuaW1hdGlvbikge3BpY19hbmkoKX0NCmBgYA0KDQo8YnI+DQoNCjxpbWcgc3JjPSJzYW1wbGVhbmkxLS5naWYiIGFsdD0iIiB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgYWxpZ249Im1pZGRsZSI+DQoNClNlYmFnYWkgZ2FudGlueWEsIHNlc2VvcmFuZyBkYXBhdCBtZW5nZ2FtYmFyIG1lbnVydXQgZGlzdHJpYnVzaSBiZXJzeWFyYXQgJEZe4ouGJCB5YW5nIGRpZGVmaW5pc2lrYW4gc2ViYWdhaQ0KDQokJEZee1xzdGFyfSh4KSA9IFxQcihYIFxsZSB4IHwgYSA8IFggXGxlIGIpID1cZnJhY3tGKHgpLUYoYSl9e0YoYiktRihhKX0sIFwgXCAgXCBcdGV4dHtmb3IgfSBhIDwgeCBcbGUgYiAuJCQNCkRlbmdhbiBtZW5nZ3VuYWthbiBtZXRvZGUgaW52ZXJzZSB0cmFuc2Zvcm0gcGFkYSBCYWdpYW4gNi4xLjIsIGtpdGEgbWVuZGFwYXRrYW4gaGFzaWwgaW1iYW5nDQoNCiQkWF5cc3Rhcj1GXntcc3Rhci0xfVxsZWZ0KCBVIFxyaWdodCkgPSBGXnstMX1cbGVmdChGKGEpK1VcY2RvdFtGKGIpLUYoYSldXHJpZ2h0KSQkDQoNCm1lbWlsaWtpIGRpc3RyaWJ1c2kgJEbii4ZeJC4gRGlueWF0YWthbiBkZW5nYW4gY2FyYSBsYWluLCBkZWZpbmlzaWthbg0KDQokJFx0aWxkZXtVfSA9ICgxLVUpXGNkb3QgRihhKStVXGNkb3QgRihiKSQkDQoNCmRhbiBrZW11ZGlhbiBndW5ha2FuICRGXnstMX0oXHRpbGRle1V9KSQuIERlbmdhbiBwZW5kZWthdGFuIGluaSwgc2V0aWFwIHVuZGlhbiBkaWhpdHVuZy4NCg0KSGFsIGluaSBkYXBhdCBkaWthaXRrYW4gZGVuZ2FuIG1la2FuaXNtZSBwZW5nYW1iaWxhbiBzYW1wZWwga2VwZW50aW5nYW46IGtpdGEgbWVuYXJpayBsZWJpaCBzZXJpbmcgZGkgd2lsYXlhaCB5YW5nIGtpdGEgaGFyYXBrYW4gbWVtaWxpa2kga3VhbnRpdGFzIHlhbmcgbWVtaWxpa2kga2VwZW50aW5nYW4uIFRyYW5zZm9ybWFzaSBpbmkgZGFwYXQgZGlhbmdnYXAgc2ViYWdhaSAicGVydWJhaGFuIHVrdXJhbi4iDQoNCmBgYHtyfQ0KcGljX2FuaSA9IGZ1bmN0aW9uKCl7DQogIHU9c2VxKDAsNSxieT0uMDEpDQogIHBsb3QodSxwbm9ybSh1LG11LHNpZ21hKSxjb2w9IndoaXRlIix5bGFiPSIiLHhsYWI9IiIpDQogIHJlY3QoLTEsLTEsNiwyLGNvbD1yZ2IoMSwwLDAsLjIpLGJvcmRlcj1OQSkNCiAgcmVjdChhLEZhLGIsRmIsY29sPSJ3aGl0ZSIsYm9yZGVyPU5BKQ0KICBsaW5lcyh1LHBub3JtKHUsbXUsc2lnbWEpLGx3ZD0yKQ0KICBhYmxpbmUoaD1wbm9ybShjKGEsYiksbXUsc2lnbWEpLGx0eT0yLGNvbD0icmVkIikNCiAgcnUgPC0gcnVuaWYoMSkNCiAgcnV0aWxkZSA8LSAoMS1ydSkqRmErcnUqRmINCiAgc2VnbWVudHMoLTEscnV0aWxkZSxxbm9ybShydXRpbGRlLG11LHNpZ21hKSxydXRpbGRlLGNvbD0iYmx1ZSIsbHdkPTIpDQogIGFycm93cyhxbm9ybShydXRpbGRlLG11LHNpZ21hKSxydXRpbGRlLHFub3JtKHJ1dGlsZGUsbXUsc2lnbWEpLDAsY29sPSJibHVlIixsd2Q9MixsZW5ndGggPSAuMSkNCn0NCmBgYA0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KZm9yIChpIGluIDE6bnVtQW5pbWF0aW9uKSB7cGljX2FuaSgpfQ0KYGBgDQoNCjxicj4NCg0KPGltZyBzcmM9InNhbXBsZWFuaV9JU18yLS5naWYiIGFsdD0iIiB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgYWxpZ249Im1pZGRsZSI+DQoNClBhZGEgQ29udG9oIDYuNC4xLiwga2ViYWxpa2FuIGRhcmkgZGlzdHJpYnVzaSBub3JtYWwgc3VkYWggdGVyc2VkaWEgKGRhbGFtIGBSYCwgZnVuZ3NpbnlhIGFkYWxhaCBgcW5vcm1gKS4gTmFtdW4sIHVudHVrIGFwbGlrYXNpIGxhaW4sIGhhbCBpbmkgdGlkYWsgdGVyamFkaS4gS2VtdWRpYW4sIGtpdGEgY3VrdXAgbWVuZ2d1bmFrYW4gbWV0b2RlIG51bWVyaWsgdW50dWsgbWVuZW50dWthbiAkWF7ii4YkIHNlYmFnYWkgc29sdXNpIGRhcmkgcGVyc2FtYWFuICRGKFheXHN0YXIpID1cdGlsZGV7VX0kIGRpIG1hbmEgJFx0aWxkZXtVfT0oMS1VKVxjZG90IEYoYSkrVVxjZG90IEYoYikkKS4gTGloYXQga29kZSBpbHVzdHJhc2kgYmVyaWt1dCBpbmkuDQoNCmBgYHtyfQ0KcGljX2FuaSA9IGZ1bmN0aW9uKCl7DQogIHU9c2VxKDAsNSxieT0uMDEpDQogIHBsb3QodSxwbm9ybSh1LG11LHNpZ21hKSxjb2w9IndoaXRlIix5bGFiPSIiLHhsYWI9IiIpDQogIHJlY3QoLTEsLTEsNiwyLGNvbD1yZ2IoMSwwLDAsLjIpLGJvcmRlcj1OQSkNCiAgcmVjdCgyLC0xLDQsMixjb2w9IndoaXRlIixib3JkZXI9TkEpDQogIGxpbmVzKHUscG5vcm0odSxtdSxzaWdtYSksbHR5PTIpDQogIHBub3Jtc3RhciA8LSBWZWN0b3JpemUoZnVuY3Rpb24oeCl7DQogICAgeT0ocG5vcm0oeCxtdSxzaWdtYSktRmEpLyhGYi1GYSkNCiAgICBpZih4PD1hKSB5IDwtIDANCiAgICBpZih4Pj1iKSB5IDwtIDENCiAgICByZXR1cm4oeSkNCiAgICB9KQ0KICBxbm9ybXN0YXIgPC0gZnVuY3Rpb24odSkgYXMubnVtZXJpYyh1bmlyb290KChmdW5jdGlvbiAoeCkgcG5vcm1zdGFyKHgpIC0gdSksIGxvd2VyID0gMiwgdXBwZXIgPSA0KVsxXSkNCiAgbGluZXModSxwbm9ybXN0YXIodSksbHdkPTIpDQogIGFibGluZSh2PWMoMiw0KSxsdHk9Mixjb2w9InJlZCIpDQogIHJ1IDwtIHJ1bmlmKDEpDQogIHNlZ21lbnRzKC0xLHJ1LHFub3Jtc3RhcihydSkscnUsY29sPSJibHVlIixsd2Q9MikNCiAgYXJyb3dzKHFub3Jtc3RhcihydSkscnUscW5vcm1zdGFyKHJ1KSwwLGNvbD0iYmx1ZSIsbHdkPTIsbGVuZ3RoID0gLjEpDQp9DQpgYGANCg0KYGBge3IsIGV2YWwgPSBGQUxTRX0NCmZvciAoaSBpbiAxOm51bUFuaW1hdGlvbikge3BpY19hbmkoKX0NCmBgYA0KPGJyPg0KPGltZyBzcmM9InNhbXBsZWFuaV9JU18xLS5naWYiIGFsdD0iIiB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgYWxpZ249Im1pZGRsZSI+DQo=