
Latihan 12 Exepektasi Maksimal
Di kelas ini kita akan menerapkan algoritma Expectation Maximization untuk memperkirakan parameter Campuran Gaussian. Metode ini menyediakan pengklasifikasi tanpa pengawasan yang sangat berguna ketika distribusi Gaussian diasumsikan.
Misalkan kita ingin memodelkan parameter populasi yang diasumsikan sebagai salah satu dari dua populasi normal gaussian dengan probabilitas pencampuran \(π\). Itu berarti \(x_i \sim N(μ_1,Σ_1)\) dengan probabilitas \(π,x_i \sim N(μ_2,Σ_2)\) dengan probabilitas \((1-π)\). Mari tunjukkan \(θ_1=(μ_1,Σ_1)\) dan \(θ_2=(μ_2,Σ_2)\) parameter setiap populasi dan \(π\) parameter pencampuran dan \(ϕ_1\) dan \(ϕ_2\) kepadatan setiap distribusi. Harapan Maksimalisasi Algoritma dapat diringkas dalam langkah-langkah berikut:
1.Ambil tebakan awal untuk parameter tersebut \(\hat{μ_1},\hat{μ_2},\hat{Σ_1},\hat{Σ_2},\hatπ\). 2.Langkah Harapan: Hitung \[\hat{γ_i}= \frac{\hatπ ϕ_1 (y_i)}{\hatπϕ_1 (y_i) + (1-\hatπ)ϕ_2(y_i)} \] 3.Langkah Maksimalisasi: Hitung mean dan varians tertimbang \[ \begin{align*}
\hat{\mu_1} &= \dfrac{\sum_{i=1}^n\hat{\gamma}_ix_i}{\sum_{i=1}^n\hat{\gamma}_i} & \hat{\Sigma_1} = \dfrac{1}{n}\dfrac{\sum_{i=1}^n\hat{\gamma}_i(x_i-\mu_1)(x_i-\mu_1)^T}{\sum_{i=1}^n\hat{\gamma}_i}\\
\hat{\mu_2}& = \dfrac{\sum_{i=1}^n(1-\hat{\gamma}_i)x_i}{\sum_{i=1}^n(1-\hat{\gamma}_i)} & \hat{\Sigma_2} = \dfrac{1}{n}\dfrac{\sum_{i=1}^n\hat{\gamma}_i(x_i-\mu_2)(x_i-\mu_2)^T}{\sum_{i=1}^n(1-\hat{\gamma}_i)}
\end{align*}\] dan kemungkinan pencampuran \(\hat{π}=\frac{1}n ∑_i^n\hat{γ_i}\) 4.Ulangi langkah 2 dan 3 hingga konvergensi.
Latihan 1
Mensimulasikan 300 sampel Campuran Gaussian dengan probabilitas pencampuran sebesar \(1/3\) sebagai berikut:
\[ \begin{equation*}
Y_1 \sim N\bigg(\begin{bmatrix}1\\1\end{bmatrix},\begin{bmatrix}2&1\\1&1\end{bmatrix} \bigg) \qquad Y_2 \sim N\bigg(\begin{bmatrix}7\\7\end{bmatrix},\begin{bmatrix}2&2\\2&5\end{bmatrix} \bigg)
\end{equation*} \]
a.Visualisasikan distribusinya dengan plot pencar dan gunakan skala berkelanjutan untuk memisahkan populasi berdasarkan warna.
library(ggplot2)
library(mvtnorm)
Mu1 = c(1,1)
Mu2 = c(7,7)
Sigma1 = matrix(c(2, 1, 1, 1), 2,2)
Sigma2 = matrix(c(2, 2, 2, 5), 2,2)
pi = 1/3
n = 300
set.seed(2)
data = matrix(0, n, 2)
z = rep(0,n)
for (i in 1:n){
z[i] = rbinom(1,1,pi)
if (z[i] ==1){
data[i,] = rmvnorm(1, Mu1,Sigma1)
}else{
data[i,] = rmvnorm(1, Mu2,Sigma2)
}
}
to.plot = data.frame(x = data[,1],
y = data[,2],
class = z)
ggplot(to.plot)+ aes(x, y, color = class)+
geom_point()+geom_density_2d()

b.Ini mengimplementasikan algoritma EM untuk memperkirakan parameternya.
Petunjuk: Buat tebakan awal untuk \(μ_1\) dan \(μ_2\) cukup memilih dua \(y_1\) sembarang. Mulailah \(Σ_1\) dan \(Σ_2\) sebagai matriks kovarians sampel keseluruhan dan \(π\) pada nilai \(0.5\). Kriteria berhenti adalah bahwa perbedaan antara dua nilai berturut-turut dari kemungkinan log lengkap kurang dari toleransi yang diinginkan, \(|l_k-l_{k-1}|<tol\)
library(SDMTools)
# EM algorithm
Alpha = NULL
tol = 10^-2
iter = 0
Q = 0
phi1 = dmvnorm(data, mu1, sigma1)
phi2 = dmvnorm(data, mu2, sigma2)
Q_ = sum(log(pi)+log(phi1)) +
sum(log(pi)+log(phi2))
while (abs(Q-Q_)>=tol) {
iter = iter+1
Q = Q_
# Expectation step
alpha = pi*phi1/(pi*phi1+(1-pi)*phi2)
Alpha = cbind(Alpha, alpha)
# Maximization step
mu1 = apply(data, 2, function(col) wt.mean(col, alpha))
mu2 = apply(data, 2, function(col) wt.mean(col, 1-alpha))
sigma1 = cov.wt(data, wt = alpha, method = "ML")$cov
sigma2 = cov.wt(data, wt = 1-alpha, method = "ML")$cov
pi = mean(alpha)
phi1 = dmvnorm(data, mu1, sigma1)
phi2 = dmvnorm(data, mu2, sigma2)
Q_ = sum(log(pi)+log(phi1)) +
sum(log(pi)+log(phi2))
}
- Memvisualisasikan nilai yang diperoleh untuk probabilitas setiap titik dalam 8 iterasi pertama.
library(reshape)
to.plot = data.frame(x = data[,1],
y = data[,2],
iter = Alpha[,1:8])
to.plot = melt(to.plot, id.vars = c("x", "y"))
ggplot(to.plot)+aes(x,y, color = value)+
geom_point()+facet_wrap(~variable, nrow = 2)

Latihan 2
Mari gunakan algoritma EM untuk melakukan segmentasi gambar. Dalam hal ini kita akan menggunakan gambar melanoma.

Algoritma segmentasi gambar biasa mengubah larik tiga dimensi yaitu gambar menjadi matriks 3 kolom dengan jumlah baris sebanyak jumlah piksel.
## [1] 737280 3
Dalam hal ini kita akan mencari proyeksi satu dimensi dari matriks tersebut sehingga algoritma EM dapat digunakan untuk mengestimasi parameter distribusi pada proyeksi yang dihasilkan.
- Implement a function that estimates the parameters for a Gaussian blend in the one-dimensional case.
EM = function(data){
set.seed(1)
pi = 0.5
mu1 = sample(data,1)
mu2 = sample(data,1)
sd1 = sd(data)
sd2 = sd1
tol = 10^-2
iter = 0
Q = 0
phi1 = dnorm(data, mu1, sd1)
phi2 = dnorm(data, mu2, sd2)
Q_ = sum(log(pi)+log(phi1)) +
sum(log(pi)+log(phi2))
while (abs(Q-Q_)>=tol) {
iter = iter+1
Q = Q_
# Expectation step
alpha = pi*phi1/(pi*phi1+(1-pi)*phi2)
# Maximization step
mu1 = wt.mean(data, alpha)
mu2 = wt.mean(data, 1-alpha)
sd1 = wt.sd(data, wt = alpha)
sd2 = wt.sd(data, wt = 1-alpha)
pi = mean(alpha)
phi1 = dnorm(data, mu1, sd1)
phi2 = dnorm(data, mu2, sd2)
Q_ = sum(log(pi)+log(phi1)) +
sum(log(pi)+log(phi2))
}
return(alpha)
}
- It is well known in the literature that the pattern of the melanoma is usually expressed in the color blue. Run the EM algorithm using that information and display the final result.

- Gunakan proyeksi yang mempertahankan variabilitas data sebanyak mungkin (komponen pertama di PCA) dan terapkan EM. Visualisasikan hasilnya.

- Gunakan proyeksi Luminance L = (0.229,0.588.0.114) dan tampilkan hasil penerapan algoritma EM.

- Terakhir, kami akan menggunakan varian dari algoritme Pengejaran Histogram Independen. Algoritma ini menemukan proyeksi yang memaksimalkan amplitudo bimodal. Setelah proyeksi itu ditemukan, kami melanjutkan ke latihan sebelumnya.
library(modes)
# Independent Histogram Pursuit
theta = expand.grid(seq(1,360,length.out = 50),
seq(1,360,length.out = 50))
w = matrix(0,nrow(theta),3)
amplitude = rep(0, nrow(theta))
for (i in 1:nrow(theta)){
w[i,] = c(sin(theta[i,1])*cos(theta[i,2]),
sin(theta[i,1])*sin(theta[i,2]),
cos(theta[i,1]))
amplitude[i] = bimodality_amplitude(img.vect%*%w[i,], fig = F)
}
ind = which.max(amplitude)
w.opt = w[ind,]
projection = img.vect%*%w.opt
prob = EM(projection)
classification = (prob>0.5)+0
segmented.image = replicate(3,classification, simplify = T)
dim(segmented.image) = dim(img)
imageShow(segmented.image)

LS0tDQp0aXRsZTogIk9wdGltaXphdGlvbiBXZWVrIDY6IEV4cGVjdGF0aW9uIE1heGltaXphdGlvbiINCmF1dGhvcjogIkltZWxkYSBTaWFudHVyaSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGhpZ2hsaWdodDogbW9ub2Nocm9tZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIExvZ28sIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAnNDAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2dpdGh1Yi5jb20vQmFrdGktU2lyZWdhci9pbWFnZXMvYmxvYi9tYXN0ZXIvbG9nby5wbmc/cmF3PXRydWUiKQ0KYGBgDQoNCiMgTGF0aWhhbiAxMiBFeGVwZWt0YXNpIE1ha3NpbWFsDQoNCkRpIGtlbGFzIGluaSBraXRhIGFrYW4gbWVuZXJhcGthbiBhbGdvcml0bWEgRXhwZWN0YXRpb24gTWF4aW1pemF0aW9uIHVudHVrIG1lbXBlcmtpcmFrYW4gcGFyYW1ldGVyIENhbXB1cmFuIEdhdXNzaWFuLiBNZXRvZGUgaW5pIG1lbnllZGlha2FuIHBlbmdrbGFzaWZpa2FzaSB0YW5wYSBwZW5nYXdhc2FuIHlhbmcgc2FuZ2F0IGJlcmd1bmEga2V0aWthIGRpc3RyaWJ1c2kgR2F1c3NpYW4gZGlhc3Vtc2lrYW4uIA0KDQpNaXNhbGthbiBraXRhIGluZ2luIG1lbW9kZWxrYW4gcGFyYW1ldGVyIHBvcHVsYXNpIHlhbmcgZGlhc3Vtc2lrYW4gc2ViYWdhaSBzYWxhaCBzYXR1IGRhcmkgZHVhIHBvcHVsYXNpIG5vcm1hbCBnYXVzc2lhbiBkZW5nYW4gcHJvYmFiaWxpdGFzIHBlbmNhbXB1cmFuICTPgCQuIEl0dSBiZXJhcnRpICR4X2kgXHNpbSBOKM68XzEszqNfMSkkIGRlbmdhbiBwcm9iYWJpbGl0YXMgJM+ALHhfaSBcc2ltIE4ozrxfMizOo18yKSQgZGVuZ2FuIHByb2JhYmlsaXRhcyAkKDEtz4ApJC4gTWFyaSB0dW5qdWtrYW4gJM64XzE9KM68XzEszqNfMSkkIGRhbiAkzrhfMj0ozrxfMizOo18yKSQgcGFyYW1ldGVyIHNldGlhcCBwb3B1bGFzaSBkYW4gICTPgCQgcGFyYW1ldGVyIHBlbmNhbXB1cmFuIGRhbiAkz5VfMSQgZGFuICTPlV8yJCBrZXBhZGF0YW4gc2V0aWFwIGRpc3RyaWJ1c2kuIEhhcmFwYW4gTWFrc2ltYWxpc2FzaSBBbGdvcml0bWEgZGFwYXQgZGlyaW5na2FzIGRhbGFtIGxhbmdrYWgtbGFuZ2thaCBiZXJpa3V0Og0KDQogIDEuQW1iaWwgdGViYWthbiBhd2FsIHVudHVrIHBhcmFtZXRlciB0ZXJzZWJ1dCAkXGhhdHvOvF8xfSxcaGF0e868XzJ9LFxoYXR7zqNfMX0sXGhhdHvOo18yfSxcaGF0z4AkLg0KICAyLkxhbmdrYWggSGFyYXBhbjogSGl0dW5nIA0KICAkJFxoYXR7zrNfaX09IFxmcmFje1xoYXTPgCDPlV8xICh5X2kpfXtcaGF0z4DPlV8xICh5X2kpICsgKDEtXGhhdM+AKc+VXzIoeV9pKX0gJCQNCiAgMy5MYW5na2FoIE1ha3NpbWFsaXNhc2k6IEhpdHVuZyBtZWFuIGRhbiB2YXJpYW5zIHRlcnRpbWJhbmcgDQogICQkIFxiZWdpbnthbGlnbip9DQogXGhhdHtcbXVfMX0gJj0gXGRmcmFje1xzdW1fe2k9MX1eblxoYXR7XGdhbW1hfV9peF9pfXtcc3VtX3tpPTF9Xm5caGF0e1xnYW1tYX1faX0gJiBcaGF0e1xTaWdtYV8xfSA9IFxkZnJhY3sxfXtufVxkZnJhY3tcc3VtX3tpPTF9Xm5caGF0e1xnYW1tYX1faSh4X2ktXG11XzEpKHhfaS1cbXVfMSleVH17XHN1bV97aT0xfV5uXGhhdHtcZ2FtbWF9X2l9XFwNCiBcaGF0e1xtdV8yfSYgPSBcZGZyYWN7XHN1bV97aT0xfV5uKDEtXGhhdHtcZ2FtbWF9X2kpeF9pfXtcc3VtX3tpPTF9Xm4oMS1caGF0e1xnYW1tYX1faSl9ICYgXGhhdHtcU2lnbWFfMn0gPSBcZGZyYWN7MX17bn1cZGZyYWN7XHN1bV97aT0xfV5uXGhhdHtcZ2FtbWF9X2koeF9pLVxtdV8yKSh4X2ktXG11XzIpXlR9e1xzdW1fe2k9MX1ebigxLVxoYXR7XGdhbW1hfV9pKX0NClxlbmR7YWxpZ24qfSQkIA0KICBkYW4ga2VtdW5na2luYW4gcGVuY2FtcHVyYW4gJFxoYXR7z4B9PVxmcmFjezF9biDiiJFfaV5uXGhhdHvOs19pfSQNCiAgNC5VbGFuZ2kgbGFuZ2thaCAyIGRhbiAzIGhpbmdnYSBrb252ZXJnZW5zaS4NCg0KIyMgTGF0aWhhbiAxDQoNCk1lbnNpbXVsYXNpa2FuIDMwMCBzYW1wZWwgQ2FtcHVyYW4gR2F1c3NpYW4gZGVuZ2FuIHByb2JhYmlsaXRhcyBwZW5jYW1wdXJhbiBzZWJlc2FyICQxLzMkIHNlYmFnYWkgYmVyaWt1dDoNCg0KJCQgXGJlZ2lue2VxdWF0aW9uKn0NCllfMSBcc2ltIE5cYmlnZyhcYmVnaW57Ym1hdHJpeH0xXFwxXGVuZHtibWF0cml4fSxcYmVnaW57Ym1hdHJpeH0yJjFcXDEmMVxlbmR7Ym1hdHJpeH0gXGJpZ2cpIFxxcXVhZCBZXzIgXHNpbSBOXGJpZ2coXGJlZ2lue2JtYXRyaXh9N1xcN1xlbmR7Ym1hdHJpeH0sXGJlZ2lue2JtYXRyaXh9MiYyXFwyJjVcZW5ke2JtYXRyaXh9IFxiaWdnKSANClxlbmR7ZXF1YXRpb24qfSAkJA0KDQogIGEuVmlzdWFsaXNhc2lrYW4gZGlzdHJpYnVzaW55YSBkZW5nYW4gcGxvdCBwZW5jYXIgZGFuIGd1bmFrYW4gc2thbGEgYmVya2VsYW5qdXRhbiB1bnR1ayBtZW1pc2Foa2FuIHBvcHVsYXNpIGJlcmRhc2Fya2FuIHdhcm5hLg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KG12dG5vcm0pDQoNCk11MSA9IGMoMSwxKQ0KTXUyID0gYyg3LDcpDQpTaWdtYTEgPSBtYXRyaXgoYygyLCAxLCAxLCAxKSwgMiwyKQ0KU2lnbWEyID0gbWF0cml4KGMoMiwgMiwgMiwgNSksIDIsMikNCg0KcGkgPSAxLzMNCm4gPSAzMDANCg0Kc2V0LnNlZWQoMikNCmRhdGEgPSBtYXRyaXgoMCwgbiwgMikNCnogPSByZXAoMCxuKQ0KZm9yIChpIGluIDE6bil7DQogIHpbaV0gPSByYmlub20oMSwxLHBpKQ0KICBpZiAoeltpXSA9PTEpew0KICAgIGRhdGFbaSxdID0gcm12bm9ybSgxLCBNdTEsU2lnbWExKQ0KICB9ZWxzZXsNCiAgICBkYXRhW2ksXSA9IHJtdm5vcm0oMSwgTXUyLFNpZ21hMikNCiAgfQ0KfQ0KDQp0by5wbG90ID0gZGF0YS5mcmFtZSh4ID0gZGF0YVssMV0sIA0KICAgICAgICAgICAgICAgICAgeSA9IGRhdGFbLDJdLCANCiAgICAgICAgICAgICAgICAgIGNsYXNzID0gIHopDQpnZ3Bsb3QodG8ucGxvdCkrIGFlcyh4LCB5LCBjb2xvciA9IGNsYXNzKSsNCiAgZ2VvbV9wb2ludCgpK2dlb21fZGVuc2l0eV8yZCgpDQpgYGANCg0KICBiLkluaSBtZW5naW1wbGVtZW50YXNpa2FuIGFsZ29yaXRtYSBFTSB1bnR1ayBtZW1wZXJraXJha2FuIHBhcmFtZXRlcm55YS4NCiAgDQpQZXR1bmp1azogQnVhdCB0ZWJha2FuIGF3YWwgdW50dWsgJM68XzEkIGRhbiAkzrxfMiQgY3VrdXAgbWVtaWxpaCBkdWEgJHlfMSQgc2VtYmFyYW5nLiBNdWxhaWxhaCAkzqNfMSQgZGFuICTOo18yJCBzZWJhZ2FpIG1hdHJpa3Mga292YXJpYW5zIHNhbXBlbCBrZXNlbHVydWhhbiBkYW4gJM+AJCBwYWRhIG5pbGFpICQwLjUkLiBLcml0ZXJpYSBiZXJoZW50aSBhZGFsYWggYmFod2EgcGVyYmVkYWFuIGFudGFyYSBkdWEgbmlsYWkgYmVydHVydXQtdHVydXQgZGFyaSBrZW11bmdraW5hbiBsb2cgbGVuZ2thcCBrdXJhbmcgZGFyaSB0b2xlcmFuc2kgeWFuZyBkaWluZ2lua2FuLCAkfGxfay1sX3trLTF9fDx0b2wkDQoNCmBgYHtyfQ0KI2luaXRpYWwgdmFsdWVzDQpwaSA9IDAuNQ0KbXUxID0gZGF0YVsxLF0NCm11MiA9IGRhdGFbMTUwLF0NCnNpZ21hMSA9IGNvdihkYXRhKQ0Kc2lnbWEyID0gY292KGRhdGEpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KFNETVRvb2xzKQ0KDQojIEVNIGFsZ29yaXRobQ0KQWxwaGEgPSBOVUxMDQp0b2wgPSAxMF4tMg0KaXRlciA9IDANClEgPSAwDQpwaGkxID0gZG12bm9ybShkYXRhLCBtdTEsIHNpZ21hMSkNCnBoaTIgPSBkbXZub3JtKGRhdGEsIG11Miwgc2lnbWEyKQ0KUV8gPSBzdW0obG9nKHBpKStsb2cocGhpMSkpICsNCiAgc3VtKGxvZyhwaSkrbG9nKHBoaTIpKQ0KDQp3aGlsZSAoYWJzKFEtUV8pPj10b2wpIHsNCiAgDQogIGl0ZXIgPSBpdGVyKzENCiAgUSA9IFFfDQogIA0KICAjIEV4cGVjdGF0aW9uIHN0ZXAgDQphbHBoYSA9IHBpKnBoaTEvKHBpKnBoaTErKDEtcGkpKnBoaTIpDQpBbHBoYSA9IGNiaW5kKEFscGhhLCBhbHBoYSkNCiAgDQogICMgTWF4aW1pemF0aW9uIHN0ZXANCm11MSA9IGFwcGx5KGRhdGEsIDIsIGZ1bmN0aW9uKGNvbCkgd3QubWVhbihjb2wsIGFscGhhKSkNCm11MiA9IGFwcGx5KGRhdGEsIDIsIGZ1bmN0aW9uKGNvbCkgd3QubWVhbihjb2wsIDEtYWxwaGEpKQ0Kc2lnbWExID0gY292Lnd0KGRhdGEsIHd0ID0gYWxwaGEsIG1ldGhvZCA9ICJNTCIpJGNvdg0Kc2lnbWEyID0gY292Lnd0KGRhdGEsIHd0ID0gMS1hbHBoYSwgbWV0aG9kID0gIk1MIikkY292DQpwaSA9IG1lYW4oYWxwaGEpDQogIA0KcGhpMSA9IGRtdm5vcm0oZGF0YSwgbXUxLCBzaWdtYTEpDQpwaGkyID0gZG12bm9ybShkYXRhLCBtdTIsIHNpZ21hMikNClFfID0gc3VtKGxvZyhwaSkrbG9nKHBoaTEpKSArDQogICAgIHN1bShsb2cocGkpK2xvZyhwaGkyKSkNCn0NCg0KYGBgDQoNCiAgYy4gTWVtdmlzdWFsaXNhc2lrYW4gbmlsYWkgeWFuZyBkaXBlcm9sZWggdW50dWsgcHJvYmFiaWxpdGFzIHNldGlhcCB0aXRpayBkYWxhbSA4IGl0ZXJhc2kgcGVydGFtYS4NCmBgYHtyfQ0KbGlicmFyeShyZXNoYXBlKQ0KDQp0by5wbG90ID0gZGF0YS5mcmFtZSh4ID0gZGF0YVssMV0sDQogICAgICAgICAgICAgICAgICAgICB5ID0gZGF0YVssMl0sDQogICAgICAgICAgICAgICAgICAgICBpdGVyID0gQWxwaGFbLDE6OF0pDQp0by5wbG90ID0gbWVsdCh0by5wbG90LCBpZC52YXJzID0gYygieCIsICJ5IikpDQoNCmdncGxvdCh0by5wbG90KSthZXMoeCx5LCBjb2xvciA9IHZhbHVlKSsNCiBnZW9tX3BvaW50KCkrZmFjZXRfd3JhcCh+dmFyaWFibGUsIG5yb3cgPSAyKQ0KYGBgDQoNCiMjIExhdGloYW4gMiANCg0KTWFyaSBndW5ha2FuIGFsZ29yaXRtYSBFTSB1bnR1ayBtZWxha3VrYW4gc2VnbWVudGFzaSBnYW1iYXIuIERhbGFtIGhhbCBpbmkga2l0YSBha2FuIG1lbmdndW5ha2FuIGdhbWJhciBtZWxhbm9tYS4NCg0KYGBge3J9DQpsaWJyYXJ5KE9wZW5JbWFnZVIpDQppbWcgPSByZWFkSW1hZ2UoIkM6Ly9Vc2Vycy9JTUVMREEgU0lBTlRVUkkvRG93bmxvYWRzL01lbGFub21hYS5qcGciKQ0KaW1hZ2VTaG93KGltZykNCmBgYA0KDQpBbGdvcml0bWEgc2VnbWVudGFzaSBnYW1iYXIgYmlhc2EgbWVuZ3ViYWggbGFyaWsgdGlnYSBkaW1lbnNpIHlhaXR1IGdhbWJhciBtZW5qYWRpIG1hdHJpa3MgMyBrb2xvbSBkZW5nYW4ganVtbGFoIGJhcmlzIHNlYmFueWFrIGp1bWxhaCBwaWtzZWwuDQoNCmBgYHtyfQ0KaW1nLnZlY3QgPSBhcHBseShpbWcsIDMsIGFzLnZlY3RvciApDQpkaW0oaW1nLnZlY3QpDQpgYGANCkRhbGFtIGhhbCBpbmkga2l0YSBha2FuIG1lbmNhcmkgcHJveWVrc2kgc2F0dSBkaW1lbnNpIGRhcmkgbWF0cmlrcyB0ZXJzZWJ1dCBzZWhpbmdnYSBhbGdvcml0bWEgRU0gZGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbmdlc3RpbWFzaSBwYXJhbWV0ZXIgZGlzdHJpYnVzaSBwYWRhIHByb3lla3NpIHlhbmcgZGloYXNpbGthbi4NCg0KICBhLiBJbXBsZW1lbnQgYSBmdW5jdGlvbiB0aGF0IGVzdGltYXRlcyB0aGUgcGFyYW1ldGVycyBmb3IgYSBHYXVzc2lhbiBibGVuZCBpbiB0aGUgb25lLWRpbWVuc2lvbmFsIGNhc2UuDQpgYGB7cn0NCkVNID0gZnVuY3Rpb24oZGF0YSl7DQogIA0KICBzZXQuc2VlZCgxKQ0KICBwaSA9IDAuNQ0KICBtdTEgPSBzYW1wbGUoZGF0YSwxKQ0KICBtdTIgPSBzYW1wbGUoZGF0YSwxKQ0KICBzZDEgPSBzZChkYXRhKQ0KICBzZDIgPSBzZDENCiAgDQogIHRvbCA9IDEwXi0yDQogIGl0ZXIgPSAwDQogIFEgPSAwDQogIHBoaTEgPSBkbm9ybShkYXRhLCBtdTEsIHNkMSkNCiAgcGhpMiA9IGRub3JtKGRhdGEsIG11Miwgc2QyKQ0KICBRXyA9IHN1bShsb2cocGkpK2xvZyhwaGkxKSkgKw0KICAgIHN1bShsb2cocGkpK2xvZyhwaGkyKSkNCiAgDQogIHdoaWxlIChhYnMoUS1RXyk+PXRvbCkgew0KICAgIA0KICAgIGl0ZXIgPSBpdGVyKzENCiAgICBRID0gUV8NCiAgICANCiAgICAjIEV4cGVjdGF0aW9uIHN0ZXAgDQogICAgYWxwaGEgPSBwaSpwaGkxLyhwaSpwaGkxKygxLXBpKSpwaGkyKQ0KICAgIA0KICAgICMgTWF4aW1pemF0aW9uIHN0ZXANCiAgICBtdTEgPSB3dC5tZWFuKGRhdGEsIGFscGhhKQ0KICAgIG11MiA9IHd0Lm1lYW4oZGF0YSwgMS1hbHBoYSkNCiAgICBzZDEgPSB3dC5zZChkYXRhLCB3dCA9IGFscGhhKQ0KICAgIHNkMiA9IHd0LnNkKGRhdGEsIHd0ID0gMS1hbHBoYSkNCiAgICBwaSA9IG1lYW4oYWxwaGEpDQogICAgDQogICAgcGhpMSA9IGRub3JtKGRhdGEsIG11MSwgc2QxKQ0KICAgIHBoaTIgPSBkbm9ybShkYXRhLCBtdTIsIHNkMikNCiAgICBRXyA9IHN1bShsb2cocGkpK2xvZyhwaGkxKSkgKw0KICAgICAgc3VtKGxvZyhwaSkrbG9nKHBoaTIpKQ0KICB9DQogICAgcmV0dXJuKGFscGhhKQ0KIH0NCmBgYA0KDQogIGIuIEl0IGlzIHdlbGwga25vd24gaW4gdGhlIGxpdGVyYXR1cmUgdGhhdCB0aGUgcGF0dGVybiBvZiB0aGUgbWVsYW5vbWEgaXMgdXN1YWxseSBleHByZXNzZWQgaW4gdGhlIGNvbG9yIGJsdWUuIFJ1biB0aGUgRU0gYWxnb3JpdGhtIHVzaW5nIHRoYXQgaW5mb3JtYXRpb24gYW5kIGRpc3BsYXkgdGhlIGZpbmFsIHJlc3VsdC4NCmBgYHtyfQ0KIyBCbHVlIGFycmF5ICsgRU0NCmJsdWUgPSBpbWcudmVjdFssM10NCnByb2IgPSBFTShibHVlKQ0KY2xhc3NpZmljYXRpb24gPSAocHJvYj4wLjUpKzANCnNlZ21lbnRlZC5pbWFnZSA9IHJlcGxpY2F0ZSgzLGNsYXNzaWZpY2F0aW9uLCBzaW1wbGlmeSA9IFQpDQpkaW0oc2VnbWVudGVkLmltYWdlKSA9IGRpbShpbWcpICANCmltYWdlU2hvdyhzZWdtZW50ZWQuaW1hZ2UpDQpgYGANCg0KICBjLiBHdW5ha2FuIHByb3lla3NpIHlhbmcgbWVtcGVydGFoYW5rYW4gdmFyaWFiaWxpdGFzIGRhdGEgc2ViYW55YWsgbXVuZ2tpbiAoa29tcG9uZW4gcGVydGFtYSBkaSBQQ0EpIGRhbiB0ZXJhcGthbiBFTS4gVmlzdWFsaXNhc2lrYW4gaGFzaWxueWEuDQpgYGB7cn0NCiMgUENBICsgRU0NCnMuaW1nLnZlY3QgPSBzY2FsZShpbWcudmVjdCkNCnNpZ21hID0gY292KGltZy52ZWN0KQ0KZWlnID0gZWlnZW4oc2lnbWEpDQplaWdlbnZlY3RvcnMgPSBlaWckdmVjdG9yc1ssMV0NCnByb2plY3Rpb24gPSBzLmltZy52ZWN0JSolIGVpZ2VudmVjdG9ycw0KDQpwcm9iID0gRU0ocHJvamVjdGlvbikNCmNsYXNzaWZpY2F0aW9uID0gKHByb2I+MC41KSswDQpzZWdtZW50ZWQuaW1hZ2UgPSByZXBsaWNhdGUoMyxjbGFzc2lmaWNhdGlvbiwgc2ltcGxpZnkgPSBUKQ0KZGltKHNlZ21lbnRlZC5pbWFnZSkgPSBkaW0oaW1nKQ0KaW1hZ2VTaG93KHNlZ21lbnRlZC5pbWFnZSkNCmBgYA0KDQogIGQuIEd1bmFrYW4gcHJveWVrc2kgTHVtaW5hbmNlIEwgPSAoMC4yMjksMC41ODguMC4xMTQpIGRhbiB0YW1waWxrYW4gaGFzaWwgcGVuZXJhcGFuIGFsZ29yaXRtYSBFTS4NCmBgYHtyfQ0KIyBMdW1pbmFuY2UgKyBFTQ0KbHVtaW5hbmNlID0gYygwLjI5OSwgMC41ODcsIDAuMTE0KQ0KDQpwcm9qZWN0aW9uID0gaW1nLnZlY3QlKiVsdW1pbmFuY2UNCg0KcHJvYiA9IEVNKHByb2plY3Rpb24pDQpjbGFzc2lmaWNhdGlvbiA9IChwcm9iPjAuNSkrMA0Kc2VnbWVudGVkLmltYWdlID0gcmVwbGljYXRlKDMsY2xhc3NpZmljYXRpb24sIHNpbXBsaWZ5ID0gVCkNCmRpbShzZWdtZW50ZWQuaW1hZ2UpID0gZGltKGltZykgIA0KaW1hZ2VTaG93KHNlZ21lbnRlZC5pbWFnZSkNCmBgYA0KICANCiAgZS4gVGVyYWtoaXIsIGthbWkgYWthbiBtZW5nZ3VuYWthbiB2YXJpYW4gZGFyaSBhbGdvcml0bWUgUGVuZ2VqYXJhbiBIaXN0b2dyYW0gSW5kZXBlbmRlbi4gQWxnb3JpdG1hIGluaSBtZW5lbXVrYW4gcHJveWVrc2kgeWFuZyBtZW1ha3NpbWFsa2FuIGFtcGxpdHVkbyBiaW1vZGFsLiBTZXRlbGFoIHByb3lla3NpIGl0dSBkaXRlbXVrYW4sIGthbWkgbWVsYW5qdXRrYW4ga2UgbGF0aWhhbiBzZWJlbHVtbnlhLg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0NCmxpYnJhcnkobW9kZXMpDQoNCiMgSW5kZXBlbmRlbnQgSGlzdG9ncmFtIFB1cnN1aXQNCnRoZXRhID0gZXhwYW5kLmdyaWQoc2VxKDEsMzYwLGxlbmd0aC5vdXQgPSA1MCksIA0KICAgICAgICAgICAgICAgICAgICBzZXEoMSwzNjAsbGVuZ3RoLm91dCA9IDUwKSkNCg0KdyA9IG1hdHJpeCgwLG5yb3codGhldGEpLDMpDQphbXBsaXR1ZGUgPSByZXAoMCwgbnJvdyh0aGV0YSkpDQpmb3IgKGkgaW4gMTpucm93KHRoZXRhKSl7DQogIHdbaSxdID0gYyhzaW4odGhldGFbaSwxXSkqY29zKHRoZXRhW2ksMl0pLA0KICAgICAgICAgICAgc2luKHRoZXRhW2ksMV0pKnNpbih0aGV0YVtpLDJdKSwNCiAgICAgICAgICAgY29zKHRoZXRhW2ksMV0pKQ0KICBhbXBsaXR1ZGVbaV0gPSBiaW1vZGFsaXR5X2FtcGxpdHVkZShpbWcudmVjdCUqJXdbaSxdLCBmaWcgPSBGKQ0KfQ0KDQppbmQgPSB3aGljaC5tYXgoYW1wbGl0dWRlKQ0Kdy5vcHQgPSB3W2luZCxdDQpwcm9qZWN0aW9uID0gaW1nLnZlY3QlKiV3Lm9wdA0KDQpwcm9iID0gRU0ocHJvamVjdGlvbikNCmNsYXNzaWZpY2F0aW9uID0gKHByb2I+MC41KSswDQpzZWdtZW50ZWQuaW1hZ2UgPSByZXBsaWNhdGUoMyxjbGFzc2lmaWNhdGlvbiwgc2ltcGxpZnkgPSBUKQ0KZGltKHNlZ21lbnRlZC5pbWFnZSkgPSBkaW0oaW1nKSAgDQppbWFnZVNob3coc2VnbWVudGVkLmltYWdlKQ0KYGBgDQoNCg==