演習の概要

注意事項

  • 慶應義塾大学SFCで開講している「環境ガバナンスのデータサイエンス」「空間モデリング特論」の授業履修者向けの演習用ページです。
  • 必ずしも全てのバージョンのRやOSで動作確認を行っていません。この演習用ページを作成している段階では、R3.5.3を使っています。
  • Rの更新などにより、Rコードなどを予告無しに変更する場合があります。

データ分析の準備

データのダウンロード

  • 演習で用いるデータはここからダウンロードしてください。
  • ここでは./直下にgisdataフォルダを作成し、setwd("gisdata")とディレクトリを指定しています

パッケージのインストール

  • install.packages()でパッケージをインストールし、library()でパッケージを呼び出す
  • ここでは以下のパッケージを用います
install.packages("spdep")
install.packages("spatstat")
install.packages("splancs")
install.packages("spatialkernel")
install.packages("spatial")
install.packages("gstat", dependencies = T)
install.packages("sp")
library(spdep)
library(spatstat)
library(splancs)
library(spatialkernel)
library(spatial)
library(gstat)
library(sp)

以下の方法でパイプ %>% の優先順位を高める

needs::prioritize(magrittr)

空間内挿

カーネル密度関数

まず、カーネル密度関数の基本となる、ガウス関数、イパクニコフ関数、四次関数を図示してみよう。 #### ガウス関数

plot(density(0, kernel="gaussian", bw=1), 
     main="", xlab="", ylab="",lwd=2, cex.axis=1)

イパクニコフ関数

plot(density(0, kernel="epanechnikov", bw=1), 
     main="", xlab="", ylab="",lwd=2, cex.axis=1)

四次関数

plot(density(0, kernel="biweight", bw=1), 
     main="", xlab="", ylab="",lwd=2, cex.axis=1)

バンド幅とカーネル密度関数(合成関数)との関係

バンド幅を変えた場合にカーネル密度関数の合成関数がどのように変化するかを見てみよう。

ここでは、ガウス関数を用い、バンド幅=1とした場合の例を示す。

# (a) バンド幅=1
ss <-1
# ss <-0.7    (b) バンド幅=0.7
# ss <-2      (c) バンド幅=2
gau.all <- function(x){
1/sqrt(2*pi*ss)*exp(-x^2/(2*ss^2)) + 
1/sqrt(2*pi*ss)*exp(-(x+4)^2/(2*ss^2)) +
1/sqrt(2*pi*ss)*exp(-(x-2)^2/(2*ss^2)) +
1/sqrt(2*pi*ss)*exp(-(x-5)^2/(2*ss^2)) +
1/sqrt(2*pi*ss)*exp(-(x+1.5)^2/(2*ss^2))}
curve(gau.all, xlim=c(-10, 10), ylim=c(0, 0.8), main="", xlab="", ylab="",
lwd=8, cex.axis=1.8)
#
gau1 <-  function(x){1/sqrt(2*pi*ss)*exp(-x^2/(2*ss^2))}
gau2 <-  function(x){1/sqrt(2*pi*ss)*exp(-(x+4)^2/(2*ss^2))}
gau3 <-  function(x){1/sqrt(2*pi*ss)*exp(-(x-2)^2/(2*ss^2))}
gau4 <-  function(x){1/sqrt(2*pi*ss)*exp(-(x-5)^2/(2*ss^2))}
gau5 <-  function(x){1/sqrt(2*pi*ss)*exp(-(x+1.5)^2/(2*ss^2))}
#
curve(gau1, lwd=4, add=TRUE)
curve(gau2, lwd=4, add=TRUE)
curve(gau3, lwd=4, add=TRUE)
curve(gau4, lwd=4, add=TRUE)
curve(gau5, lwd=4, add=TRUE)

2次元平面空間上での密度関数

次に、2次元平面空間上でバンド幅を変えた場合の影響を見てみよう。

ここでは、ガウス関数を用い、バンド幅=1年経例を示す。

x <- runif(50)*10
y <- runif(50)*10
xy <- cbind(x, y)
poly0 <- cbind(c(0,10,10,0), c(0,0,10,10))
# バンド幅=1
image(splancs::kernel2d(xy, poly0, h0=1, nx=100, ny=100),
      col=gray((0:20)/20),cex.axis=1.8)
Xrange is  0 10 
Yrange is  0 10 
Doing quartic kernel

# バンド幅=0.5
# image(kernel2d(xy, poly0, h0=0.5, nx=100, ny=100), col=gray((0:20)/20),cex.axis=1.5)
# バンド幅=0.7
# image(kernel2d(xy, poly0, h0=0.7, nx=100, ny=100), col=gray((0:20)/20),cex.axis=1.5)

最小二乗法によるバンド幅の決定

splancsパッケージのmse2d()関数を用いて、最小二乗法によりバンド幅を決定することができる。

poly0 <- cbind(c(0,1,1,0), c(0,0,1,1))
X <- cbind(runif(50), runif(50))
Mse2d <- splancs::mse2d(splancs::as.points(X), poly0, nsmse=50, range=1)
plot(Mse2d$h[5:50],Mse2d$mse[5:50], type="l", ylab="MSE", xlab="バンド幅",
     lwd=1, cex.axis=1, cex.lab=1)
points(Mse2d$h[which.min(Mse2d$mse)], Mse2d$mse[which.min(Mse2d$mse)],
pch=1, cex=3, lwd=2)

交差検証対数尤度関数によるバンド幅の決定

spatialkernelパッケージのcvloglk() 関数を用いて、交差検証対数尤度関数によるバンド幅を決定することができる。

x <- runif(300)
y <- runif(300)
mks <- sample(c("a","b", "c"), 300, replace=TRUE)
pts <- cbind(x, y)
h <- seq(0.01, 1, by=0.01)
cv <- spatialkernel::cvloglk(pts, mks, h=h)$cv
# 図9.6
plot(h, cv, type="l", ylab="交差検証対数尤度", xlab="バンド幅",
     lwd=6, cex.axis=1.3, cex.lab=1.2)
points(h[which.max(cv)], cv[which.max(cv)], pch=1, cex=3, lwd=2)

データの読み込み

spm.shp  <- sf::st_read('tma_spm.shp')
spm <- ppp(spm.shp$X, spm.shp$Y, c(-70000, 90000), c(-110000, 30000),
           marks=spm.shp$SPM07*1000)
ward.shp <- sf::st_read('Ward.shp')
mesh.grid <- readr::read_csv('mesh.csv')
coordinates(mesh.grid) <- c("X", "Y")
mesh.grid <- as(mesh.grid, "SpatialPixelsDataFrame")

逆距離加重法

spm.idw1 <- idw(X=spm, power=2, at="pixels", se=TRUE)
plot(spm.idw1$estimate)

plot(spm.idw1$SE)

クリギング

データの準備

spm.v <- cbind(spm.shp$ID, spm.shp$X, spm.shp$Y, spm.shp$SPM07)
colnames(spm.v) <- c("ID", "X", "Y", "SPM07")
spm.v <- as.data.frame(spm.v)
sp::coordinates(spm.v) <- c("X","Y")

ヴァリオグラム・モデル

ヴァリオグラム雲と標本ヴァリオグラム

まずは定数項を考慮したモデルを推定し、ヴァリオグラム雲と標本ヴァリオグラムを可視化してみよう。

spm.var1 <- variogram(SPM07*1000~X+Y, data=spm.v) 

variogram()関数のcloud=TRUEとすると、ヴァリオグラム雲を作成できる。

var.cld <- variogram(SPM07*1000~X+Y, data=spm.v, cloud=TRUE)
plot(var.cld$dist, var.cld$gamma, pch=19, cex=1, 
  xlab="distance", ylab="gamma", cex.axis=1, cex.lab=1)

また標本ヴァリオグラムは以下のように可視化できる。

plot(spm.var1$dist, spm.var1$gamma, pch=1, lwd=1, cex=1, ylim=c(0, 80),
  xlab="distance", ylab="gamma", cex.axis=1, cex.lab=1)

指数モデル
plot(variogramLine(vgm(psill=25, model="Exp", range=28000, nugget=45),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 

#spm.model1 <- vgm(psill=25, model="Exp", range=28000, nugget=45)
#plot(spm.var1, spm.model1, cex=1.5, lwd=4)
球形モデル
plot(variogramLine(vgm(psill=25, model="Sph", range=60000, nugget=45),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 

# spm.model2 <- vgm(psill=25, model="Sph", range=60000, nugget=45)
# plot(spm.var1, spm.model2, cex=1.5, lwd=4)
線形モデル
plot(variogramLine(vgm(psill=25, model="Lin", range=56000, nugget=45),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 

# spm.model3 <- vgm(psill=25, model="Lin", range=56000, nugget=45)
# plot(spm.var1, spm.model3, cex=1.5, lwd=4)
ガウスモデル
plot(variogramLine(vgm(psill=20, model="Gau", range=35000, nugget=50),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 

# spm.model4 <- vgm(psill=20, model="Gau", range=35000, nugget=50)
# plot(spm.var1, spm.model4, cex=1.5, lwd=4)
ナゲット効果モデル
plot(variogramLine(vgm(psill=0, model="Nug", range=0, nugget=70),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 

# spm.model5 <- vgm(psill=0, model="Nug", nugget=70)
# plot(spm.var1, spm.model5, cex=1.5, lwd=4) 
Maternモデル
plot(variogramLine(vgm(psill=25, model="Mat", range=30000, nugget=45),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 

# spm.model6 <- vgm(psill=25, model="Mat", range=30000, nugget=45)
# plot(spm.var1, spm.model6, cex=1.5, lwd=4)
推定方法によるヴァリオグラム・モデルの違い

ヴァリオグラム・モデルは推定方法によって推定結果が異なる場合がある。ここでは、球形モデルを例に、重み付き最小二乗法、通常最小二乗法、制限付き最尤法の3つの推定方法による推定結果の違いを示そう。

spm.model2 <- vgm(psill=25, model="Sph", range=60000, nugget=45)
spm.fit <- fit.variogram(spm.var1, spm.model2)
# 球形モデル:WLS
fit.variogram(spm.var1, spm.model2, fit.method=7)
# 球形モデル:OLS
fit.variogram(spm.var1, spm.model2, fit.method=6)
# 制限付き最尤法
fit.variogram.reml(SPM07*1000~X+Y,data=spm.v, model=vgm(25, "Sph", 60000, 45))
# WLS
plot(variogramLine(vgm(psill=25, model="Sph", range=60000, nugget=45),
70000, 100), type="l", cex=1.5, lwd=4, ylab="semivariance", xlab="distance",
cex.axis=1.2, cex.lab=1.2, ylim=c(40,80))
# OLS
lines(variogramLine(vgm(psill=25.15494, model="Sph", range=93784.44, nugget=48.34058), 100000, 100), cex=1.5, lwd=4,lty=2)
# REML
lines(variogramLine(vgm(psill=28.94858, model="Sph", range=60000, nugget=51.51024), 100000, 100), cex=1.5, lwd=4,lty=3)
# バリオグラム
points(spm.var1$dist, spm.var1$gamma, cex=1.5, lwd=2) 
# 凡例
legend("bottomright", legend=c("WLS", "OLS", "REML"), lty=c(1,2,3), lwd=c(4,4,4), cex=1.5)

異方性モデリング

variogram()関数のalphaを指定すると、異方性(anisotropy)を考慮したヴァリオグラム・モデルを推定できる。

spm.var2 <- variogram(SPM07*1000~X+Y, data=spm.v, alpha=0:3*90)
plot(spm.var2)

また、anisを指定して異方性モデルを推定することもできる。

spm.anis1 <- vgm(psill=25, model="Gau", range=35000, nugget=50, anis=c(0, 0.8))
plot(spm.var2, spm.anis1)

クリギングによる内挿補間

ヴァリオグラム・モデルに指数モデルを採用し、クリギングに空間内挿補間を適用しよう。

ここでは、単純クリギング、通常クリギング、普遍クリギング、ブロック・クリギングの適用例を示す。 ##### 単純クリギング

#spm.model1 <- vgm(psill=25, model="Exp", range=28000, nugget=45)
spm.krige1 <- krige(SPM07*1000~1, spm.v, mesh.grid, model=spm.model1,
                   beta=mean(spm.v$SPM07*1000))
[using simple kriging]
spplot(spm.krige1["var1.pred"], main="Simple Kriging Prediction")

spplot(spm.krige1["var1.var"], main="Simple Kriging Variance")

通常クリギング
spm.krige2 <- krige(SPM07*1000~1, spm.v, mesh.grid, model=spm.model1)
[using ordinary kriging]
spplot(spm.krige2["var1.pred"], main="Ordinary Kriging Prediction")

spplot(spm.krige2["var1.var"], main="Ordinary Kriging Variance")

普遍クリギング
spm.krige3 <- krige(SPM07*1000~X+Y, spm.v, mesh.grid, model=spm.model1, 
                    block=c(250,250))
[using universal kriging]
spplot(spm.krige3["var1.pred"], main="Universal Kriging Prediction")

spplot(spm.krige3["var1.var"], main="Universal Kriging Variance")

LS0tCnRpdGxlOiAi56m66ZaT44Oi44OH44Oq44Oz44KwUua8lOe/kjciCiNhdXRob3I6ICJUb21veXVraSBGdXJ1dGFuaSIKI2RhdGU6ICIyMDE5LzMvMjkiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFKQpgYGAKCiMjIOa8lOe/kuOBruamguimgQotIOOCq+ODvOODjeODq+WvhuW6pumWouaVsOOAgeepuumWk+WGheaMvwoKIyMjIOazqOaEj+S6i+mghQotIOaFtuaHiee+qeWhvuWkp+WtplNGQ+OBp+mWi+ism+OBl+OBpuOBhOOCi+OAjOepuumWk+ODouODh+ODquODs+OCsOOAjeOBruaOiOalreWxpeS/ruiAheWQkeOBkeOBrua8lOe/kueUqOODmuODvOOCuOOBp+OBmeOAggotIOW/heOBmuOBl+OCguWFqOOBpuOBruODkOODvOOCuOODp+ODs+OBrlLjgoRPU+OBp+WLleS9nOeiuuiqjeOCkuihjOOBo+OBpuOBhOOBvuOBm+OCk+OAguOBk+OBrua8lOe/kueUqOODmuODvOOCuOOCkuS9nOaIkOOBl+OBpuOBhOOCi+autemajuOBp+OBr+OAgVIzLjUuM+OCkuS9v+OBo+OBpuOBhOOBvuOBmeOAggotIFLjga7mm7TmlrDjgarjganjgavjgojjgorjgIFS44Kz44O844OJ44Gq44Gp44KS5LqI5ZGK54Sh44GX44Gr5aSJ5pu044GZ44KL5aC05ZCI44GM44GC44KK44G+44GZ44CCIAoKIyMg44OH44O844K/5YiG5p6Q44Gu5rqW5YKZCiMjIyDjg4fjg7zjgr/jga7jg4Djgqbjg7Pjg63jg7zjg4kKLSDmvJTnv5LjgafnlKjjgYTjgovjg4fjg7zjgr/jga9b44GT44GTXShodHRwOi8vd2ViLnNmYy5rZWlvLmFjLmpwL35tYXVuei9hc2FrdXJhX3NwL2FzYWt1cmFfc3BfZGF0YV8xMTAxLnppcCnjgYvjgonjg4Djgqbjg7Pjg63jg7zjg4njgZfjgabjgY/jgaDjgZXjgYTjgIIKLSDjgZPjgZPjgafjga9gLi9g55u05LiL44GrYGdpc2RhdGFg44OV44Kp44Or44OA44KS5L2c5oiQ44GX44CBYHNldHdkKCJnaXNkYXRhIilg44Go44OH44Kj44Os44Kv44OI44Oq44KS5oyH5a6a44GX44Gm44GE44G+44GZCgojIyMg44OR44OD44Kx44O844K444Gu44Kk44Oz44K544OI44O844OrCi0gYGluc3RhbGwucGFja2FnZXMoKWDjgafjg5Hjg4PjgrHjg7zjgrjjgpLjgqTjg7Pjgrnjg4jjg7zjg6vjgZfjgIFgbGlicmFyeSgpYOOBp+ODkeODg+OCseODvOOCuOOCkuWRvOOBs+WHuuOBmQotIOOBk+OBk+OBp+OBr+S7peS4i+OBruODkeODg+OCseODvOOCuOOCkueUqOOBhOOBvuOBmQoKYGBge3IgaW5zdGFsbCBwYWNrYWdlcywgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygic3BkZXAiKQppbnN0YWxsLnBhY2thZ2VzKCJzcGF0c3RhdCIpCmluc3RhbGwucGFja2FnZXMoInNwbGFuY3MiKQppbnN0YWxsLnBhY2thZ2VzKCJzcGF0aWFsa2VybmVsIikKaW5zdGFsbC5wYWNrYWdlcygic3BhdGlhbCIpCmluc3RhbGwucGFja2FnZXMoImdzdGF0IiwgZGVwZW5kZW5jaWVzID0gVCkKaW5zdGFsbC5wYWNrYWdlcygic3AiKQpgYGAKCmBgYHtyIGxpYnJhcnksIGV2YWw9RkFMU0V9CmxpYnJhcnkoc3BkZXApCmxpYnJhcnkoc3BhdHN0YXQpCmxpYnJhcnkoc3BsYW5jcykKbGlicmFyeShzcGF0aWFsa2VybmVsKQpsaWJyYXJ5KHNwYXRpYWwpCmxpYnJhcnkoZ3N0YXQpCmxpYnJhcnkoc3ApCmBgYAoK5Lul5LiL44Gu5pa55rOV44Gn44OR44Kk44OXICU+JSDjga7lhKrlhYjpoIbkvY3jgpLpq5jjgoHjgosKYGBge3IgcGlwZV9wcmlvcml0aXplfQpuZWVkczo6cHJpb3JpdGl6ZShtYWdyaXR0cikKYGBgCgojIyDnqbrplpPlhoXmjL8KIyMjIOOCq+ODvOODjeODq+WvhuW6pumWouaVsArjgb7jgZrjgIHjgqvjg7zjg43jg6vlr4bluqbplqLmlbDjga7ln7rmnKzjgajjgarjgovjgIHjgqzjgqbjgrnplqLmlbDjgIHjgqTjg5Hjgq/jg4vjgrPjg5XplqLmlbDjgIHlm5vmrKHplqLmlbDjgpLlm7PnpLrjgZfjgabjgb/jgojjgYbjgIIKIyMjIyDjgqzjgqbjgrnplqLmlbAKYGBge3Iga2VybmVsMX0KcGxvdChkZW5zaXR5KDAsIGtlcm5lbD0iZ2F1c3NpYW4iLCBidz0xKSwgCiAgICAgbWFpbj0iIiwgeGxhYj0iIiwgeWxhYj0iIixsd2Q9MiwgY2V4LmF4aXM9MSkKYGBgCgojIyMjIOOCpOODkeOCr+ODi+OCs+ODlemWouaVsApgYGB7ciBrZXJuZWwyfQpwbG90KGRlbnNpdHkoMCwga2VybmVsPSJlcGFuZWNobmlrb3YiLCBidz0xKSwgCiAgICAgbWFpbj0iIiwgeGxhYj0iIiwgeWxhYj0iIixsd2Q9MiwgY2V4LmF4aXM9MSkKYGBgCgojIyMjIOWbm+asoemWouaVsApgYGB7ciBrZXJuZWwzfQpwbG90KGRlbnNpdHkoMCwga2VybmVsPSJiaXdlaWdodCIsIGJ3PTEpLCAKICAgICBtYWluPSIiLCB4bGFiPSIiLCB5bGFiPSIiLGx3ZD0yLCBjZXguYXhpcz0xKQpgYGAKCiMjIyMg44OQ44Oz44OJ5bmF44Go44Kr44O844ON44Or5a+G5bqm6Zai5pWw77yI5ZCI5oiQ6Zai5pWw77yJ44Go44Gu6Zai5L+CCuODkOODs+ODieW5heOCkuWkieOBiOOBn+WgtOWQiOOBq+OCq+ODvOODjeODq+WvhuW6pumWouaVsOOBruWQiOaIkOmWouaVsOOBjOOBqeOBruOCiOOBhuOBq+WkieWMluOBmeOCi+OBi+OCkuimi+OBpuOBv+OCiOOBhuOAggoK44GT44GT44Gn44Gv44CB44Ks44Km44K56Zai5pWw44KS55So44GE44CB44OQ44Oz44OJ5bmFPTHjgajjgZfjgZ/loLTlkIjjga7kvovjgpLnpLrjgZnjgIIKYGBge3Iga2VybmVsNH0KIyAoYSkg44OQ44Oz44OJ5bmFPTEKc3MgPC0xCiMgc3MgPC0wLjcgICAgKGIpIOODkOODs+ODieW5hT0wLjcKIyBzcyA8LTIgICAgICAoYykg44OQ44Oz44OJ5bmFPTIKCmdhdS5hbGwgPC0gZnVuY3Rpb24oeCl7CjEvc3FydCgyKnBpKnNzKSpleHAoLXheMi8oMipzc14yKSkgKyAKMS9zcXJ0KDIqcGkqc3MpKmV4cCgtKHgrNCleMi8oMipzc14yKSkgKwoxL3NxcnQoMipwaSpzcykqZXhwKC0oeC0yKV4yLygyKnNzXjIpKSArCjEvc3FydCgyKnBpKnNzKSpleHAoLSh4LTUpXjIvKDIqc3NeMikpICsKMS9zcXJ0KDIqcGkqc3MpKmV4cCgtKHgrMS41KV4yLygyKnNzXjIpKX0KY3VydmUoZ2F1LmFsbCwgeGxpbT1jKC0xMCwgMTApLCB5bGltPWMoMCwgMC44KSwgbWFpbj0iIiwgeGxhYj0iIiwgeWxhYj0iIiwKbHdkPTgsIGNleC5heGlzPTEuOCkKIwpnYXUxIDwtICBmdW5jdGlvbih4KXsxL3NxcnQoMipwaSpzcykqZXhwKC14XjIvKDIqc3NeMikpfQpnYXUyIDwtICBmdW5jdGlvbih4KXsxL3NxcnQoMipwaSpzcykqZXhwKC0oeCs0KV4yLygyKnNzXjIpKX0KZ2F1MyA8LSAgZnVuY3Rpb24oeCl7MS9zcXJ0KDIqcGkqc3MpKmV4cCgtKHgtMileMi8oMipzc14yKSl9CmdhdTQgPC0gIGZ1bmN0aW9uKHgpezEvc3FydCgyKnBpKnNzKSpleHAoLSh4LTUpXjIvKDIqc3NeMikpfQpnYXU1IDwtICBmdW5jdGlvbih4KXsxL3NxcnQoMipwaSpzcykqZXhwKC0oeCsxLjUpXjIvKDIqc3NeMikpfQojCmN1cnZlKGdhdTEsIGx3ZD00LCBhZGQ9VFJVRSkKY3VydmUoZ2F1MiwgbHdkPTQsIGFkZD1UUlVFKQpjdXJ2ZShnYXUzLCBsd2Q9NCwgYWRkPVRSVUUpCmN1cnZlKGdhdTQsIGx3ZD00LCBhZGQ9VFJVRSkKY3VydmUoZ2F1NSwgbHdkPTQsIGFkZD1UUlVFKQpgYGAKCiMjIyMg77yS5qyh5YWD5bmz6Z2i56m66ZaT5LiK44Gn44Gu5a+G5bqm6Zai5pWwCuasoeOBq+OAge+8kuasoeWFg+W5s+mdouepuumWk+S4iuOBp+ODkOODs+ODieW5heOCkuWkieOBiOOBn+WgtOWQiOOBruW9semfv+OCkuimi+OBpuOBv+OCiOOBhuOAggoK44GT44GT44Gn44Gv44CB44Ks44Km44K56Zai5pWw44KS55So44GE44CB44OQ44Oz44OJ5bmF77yd77yR5bm057WM5L6L44KS56S644GZ44CCCmBgYHtyIGtlcm5lbDV9CnggPC0gcnVuaWYoNTApKjEwCnkgPC0gcnVuaWYoNTApKjEwCnh5IDwtIGNiaW5kKHgsIHkpCnBvbHkwIDwtIGNiaW5kKGMoMCwxMCwxMCwwKSwgYygwLDAsMTAsMTApKQojIOODkOODs+ODieW5he+8ne+8kQppbWFnZShzcGxhbmNzOjprZXJuZWwyZCh4eSwgcG9seTAsIGgwPTEsIG54PTEwMCwgbnk9MTAwKSwKICAgICAgY29sPWdyYXkoKDA6MjApLzIwKSxjZXguYXhpcz0xLjgpCiMg44OQ44Oz44OJ5bmF77ydMC41CiMgaW1hZ2Uoa2VybmVsMmQoeHksIHBvbHkwLCBoMD0wLjUsIG54PTEwMCwgbnk9MTAwKSwgY29sPWdyYXkoKDA6MjApLzIwKSxjZXguYXhpcz0xLjUpCiMg44OQ44Oz44OJ5bmF77ydMC43CiMgaW1hZ2Uoa2VybmVsMmQoeHksIHBvbHkwLCBoMD0wLjcsIG54PTEwMCwgbnk9MTAwKSwgY29sPWdyYXkoKDA6MjApLzIwKSxjZXguYXhpcz0xLjUpCmBgYAoKIyMjIyDmnIDlsI/kuozkuZfms5Xjgavjgojjgovjg5Djg7Pjg4nluYXjga7msbrlrpoKYHNwbGFuY3Ng44OR44OD44Kx44O844K444GuYG1zZTJkKClg6Zai5pWw44KS55So44GE44Gm44CB5pyA5bCP5LqM5LmX5rOV44Gr44KI44KK44OQ44Oz44OJ5bmF44KS5rG65a6a44GZ44KL44GT44Go44GM44Gn44GN44KL44CCCmBgYHtyIGtlcm5lbDZ9CnBvbHkwIDwtIGNiaW5kKGMoMCwxLDEsMCksIGMoMCwwLDEsMSkpClggPC0gY2JpbmQocnVuaWYoNTApLCBydW5pZig1MCkpCk1zZTJkIDwtIHNwbGFuY3M6Om1zZTJkKHNwbGFuY3M6OmFzLnBvaW50cyhYKSwgcG9seTAsIG5zbXNlPTUwLCByYW5nZT0xKQpwbG90KE1zZTJkJGhbNTo1MF0sTXNlMmQkbXNlWzU6NTBdLCB0eXBlPSJsIiwgeWxhYj0iTVNFIiwgeGxhYj0i44OQ44Oz44OJ5bmFIiwKICAgICBsd2Q9MSwgY2V4LmF4aXM9MSwgY2V4LmxhYj0xKQpwb2ludHMoTXNlMmQkaFt3aGljaC5taW4oTXNlMmQkbXNlKV0sIE1zZTJkJG1zZVt3aGljaC5taW4oTXNlMmQkbXNlKV0sCnBjaD0xLCBjZXg9MywgbHdkPTIpCmBgYAoKIyMjIyDkuqTlt67mpJzoqLzlr77mlbDlsKTluqbplqLmlbDjgavjgojjgovjg5Djg7Pjg4nluYXjga7msbrlrpoKYHNwYXRpYWxrZXJuZWxg44OR44OD44Kx44O844K444GuYGN2bG9nbGsoKWAg6Zai5pWw44KS55So44GE44Gm44CB5Lqk5beu5qSc6Ki85a++5pWw5bCk5bqm6Zai5pWw44Gr44KI44KL44OQ44Oz44OJ5bmF44KS5rG65a6a44GZ44KL44GT44Go44GM44Gn44GN44KL44CCCmBgYHtyIGtlcm5lbDd9CnggPC0gcnVuaWYoMzAwKQp5IDwtIHJ1bmlmKDMwMCkKbWtzIDwtIHNhbXBsZShjKCJhIiwiYiIsICJjIiksIDMwMCwgcmVwbGFjZT1UUlVFKQpwdHMgPC0gY2JpbmQoeCwgeSkKaCA8LSBzZXEoMC4wMSwgMSwgYnk9MC4wMSkKY3YgPC0gc3BhdGlhbGtlcm5lbDo6Y3Zsb2dsayhwdHMsIG1rcywgaD1oKSRjdgojIOWbszkuNgpwbG90KGgsIGN2LCB0eXBlPSJsIiwgeWxhYj0i5Lqk5beu5qSc6Ki85a++5pWw5bCk5bqmIiwgeGxhYj0i44OQ44Oz44OJ5bmFIiwKICAgICBsd2Q9NiwgY2V4LmF4aXM9MS4zLCBjZXgubGFiPTEuMikKcG9pbnRzKGhbd2hpY2gubWF4KGN2KV0sIGN2W3doaWNoLm1heChjdildLCBwY2g9MSwgY2V4PTMsIGx3ZD0yKQpgYGAKCiMjIyDjg4fjg7zjgr/jga7oqq3jgb/ovrzjgb8KYGBge3Igc3BpbnRlcnBfZGF0YSwgZXZhbD1GQUxTRX0Kc3BtLnNocCAgPC0gc2Y6OnN0X3JlYWQoJ3RtYV9zcG0uc2hwJykKc3BtIDwtIHBwcChzcG0uc2hwJFgsIHNwbS5zaHAkWSwgYygtNzAwMDAsIDkwMDAwKSwgYygtMTEwMDAwLCAzMDAwMCksCiAgICAgICAgICAgbWFya3M9c3BtLnNocCRTUE0wNyoxMDAwKQp3YXJkLnNocCA8LSBzZjo6c3RfcmVhZCgnV2FyZC5zaHAnKQptZXNoLmdyaWQgPC0gcmVhZHI6OnJlYWRfY3N2KCdtZXNoLmNzdicpCmNvb3JkaW5hdGVzKG1lc2guZ3JpZCkgPC0gYygiWCIsICJZIikKbWVzaC5ncmlkIDwtIGFzKG1lc2guZ3JpZCwgIlNwYXRpYWxQaXhlbHNEYXRhRnJhbWUiKQpgYGAKCiMjIyDpgIbot53pm6LliqDph43ms5UKYGBge3IgSURXfQpzcG0uaWR3MSA8LSBpZHcoWD1zcG0sIHBvd2VyPTIsIGF0PSJwaXhlbHMiLCBzZT1UUlVFKQpwbG90KHNwbS5pZHcxJGVzdGltYXRlKQpwbG90KHNwbS5pZHcxJFNFKQpgYGAKCiMjIyDjgq/jg6rjgq7jg7PjgrAKIyMjIyDjg4fjg7zjgr/jga7mupblgpkKYGBge3IgdmFyaW9fZGF0YX0Kc3BtLnYgPC0gY2JpbmQoc3BtLnNocCRJRCwgc3BtLnNocCRYLCBzcG0uc2hwJFksIHNwbS5zaHAkU1BNMDcpCmNvbG5hbWVzKHNwbS52KSA8LSBjKCJJRCIsICJYIiwgIlkiLCAiU1BNMDciKQpzcG0udiA8LSBhcy5kYXRhLmZyYW1lKHNwbS52KQpzcDo6Y29vcmRpbmF0ZXMoc3BtLnYpIDwtIGMoIlgiLCJZIikKYGBgCgojIyMjIOODtOOCoeODquOCquOCsOODqeODoOODu+ODouODh+ODqwojIyMjIyDjg7TjgqHjg6rjgqrjgrDjg6njg6Dpm7LjgajmqJnmnKzjg7TjgqHjg6rjgqrjgrDjg6njg6AK44G+44Ga44Gv5a6a5pWw6aCF44KS6ICD5oWu44GX44Gf44Oi44OH44Or44KS5o6o5a6a44GX44CB44O044Kh44Oq44Kq44Kw44Op44Og6Zuy44Go5qiZ5pys44O044Kh44Oq44Kq44Kw44Op44Og44KS5Y+v6KaW5YyW44GX44Gm44G/44KI44GG44CCCgpgYGB7ciBzcG0udjF9CnNwbS52YXIxIDwtIHZhcmlvZ3JhbShTUE0wNyoxMDAwflgrWSwgZGF0YT1zcG0udikgCmBgYAoKYHZhcmlvZ3JhbSgpYOmWouaVsOOBrmNsb3VkPVRSVUXjgajjgZnjgovjgajjgIHjg7TjgqHjg6rjgqrjgrDjg6njg6Dpm7LjgpLkvZzmiJDjgafjgY3jgovjgIIKYGBge3Igc3BtLnYxLmNsb3VkfQp2YXIuY2xkIDwtIHZhcmlvZ3JhbShTUE0wNyoxMDAwflgrWSwgZGF0YT1zcG0udiwgY2xvdWQ9VFJVRSkKcGxvdCh2YXIuY2xkJGRpc3QsIHZhci5jbGQkZ2FtbWEsIHBjaD0xOSwgY2V4PTEsIAogIHhsYWI9ImRpc3RhbmNlIiwgeWxhYj0iZ2FtbWEiLCBjZXguYXhpcz0xLCBjZXgubGFiPTEpCmBgYAoK44G+44Gf5qiZ5pys44O044Kh44Oq44Kq44Kw44Op44Og44Gv5Lul5LiL44Gu44KI44GG44Gr5Y+v6KaW5YyW44Gn44GN44KL44CCCmBgYHtyIHNwbS52MS5zYW1wbGV9CnBsb3Qoc3BtLnZhcjEkZGlzdCwgc3BtLnZhcjEkZ2FtbWEsIHBjaD0xLCBsd2Q9MSwgY2V4PTEsIHlsaW09YygwLCA4MCksCiAgeGxhYj0iZGlzdGFuY2UiLCB5bGFiPSJnYW1tYSIsIGNleC5heGlzPTEsIGNleC5sYWI9MSkKYGBgCgojIyMjIyDmjIfmlbDjg6Ljg4fjg6sKCmBgYHtyIHZhcmlvLm1vZGVsMX0KcGxvdCh2YXJpb2dyYW1MaW5lKHZnbShwc2lsbD0yNSwgbW9kZWw9IkV4cCIsIHJhbmdlPTI4MDAwLCBudWdnZXQ9NDUpLAo3MDAwMCwgMTAwKSwgdHlwZT0ibCIsIGNleD0xLjUsIGx3ZD00LCB5bGFiPSJzZW1pdmFyaWFuY2UiLCB4bGFiPSJkaXN0YW5jZSIsCmNleC5heGlzPTEuMiwgY2V4LmxhYj0xLjIsIHlsaW09Yyg0MCw4MCkpCnBvaW50cyhzcG0udmFyMSRkaXN0LCBzcG0udmFyMSRnYW1tYSwgY2V4PTEuNSwgbHdkPTIpIAojc3BtLm1vZGVsMSA8LSB2Z20ocHNpbGw9MjUsIG1vZGVsPSJFeHAiLCByYW5nZT0yODAwMCwgbnVnZ2V0PTQ1KQojcGxvdChzcG0udmFyMSwgc3BtLm1vZGVsMSwgY2V4PTEuNSwgbHdkPTQpCmBgYAoKCiMjIyMjIOeQg+W9ouODouODh+ODqwpgYGB7ciB2YXJpby5tb2RlbDJ9CnBsb3QodmFyaW9ncmFtTGluZSh2Z20ocHNpbGw9MjUsIG1vZGVsPSJTcGgiLCByYW5nZT02MDAwMCwgbnVnZ2V0PTQ1KSwKNzAwMDAsIDEwMCksIHR5cGU9ImwiLCBjZXg9MS41LCBsd2Q9NCwgeWxhYj0ic2VtaXZhcmlhbmNlIiwgeGxhYj0iZGlzdGFuY2UiLApjZXguYXhpcz0xLjIsIGNleC5sYWI9MS4yLCB5bGltPWMoNDAsODApKQpwb2ludHMoc3BtLnZhcjEkZGlzdCwgc3BtLnZhcjEkZ2FtbWEsIGNleD0xLjUsIGx3ZD0yKSAKIyBzcG0ubW9kZWwyIDwtIHZnbShwc2lsbD0yNSwgbW9kZWw9IlNwaCIsIHJhbmdlPTYwMDAwLCBudWdnZXQ9NDUpCiMgcGxvdChzcG0udmFyMSwgc3BtLm1vZGVsMiwgY2V4PTEuNSwgbHdkPTQpCmBgYAoKIyMjIyMg57ea5b2i44Oi44OH44OrCmBgYHtyIHZhcmlvLm1vZGVsM30KcGxvdCh2YXJpb2dyYW1MaW5lKHZnbShwc2lsbD0yNSwgbW9kZWw9IkxpbiIsIHJhbmdlPTU2MDAwLCBudWdnZXQ9NDUpLAo3MDAwMCwgMTAwKSwgdHlwZT0ibCIsIGNleD0xLjUsIGx3ZD00LCB5bGFiPSJzZW1pdmFyaWFuY2UiLCB4bGFiPSJkaXN0YW5jZSIsCmNleC5heGlzPTEuMiwgY2V4LmxhYj0xLjIsIHlsaW09Yyg0MCw4MCkpCnBvaW50cyhzcG0udmFyMSRkaXN0LCBzcG0udmFyMSRnYW1tYSwgY2V4PTEuNSwgbHdkPTIpIAojIHNwbS5tb2RlbDMgPC0gdmdtKHBzaWxsPTI1LCBtb2RlbD0iTGluIiwgcmFuZ2U9NTYwMDAsIG51Z2dldD00NSkKIyBwbG90KHNwbS52YXIxLCBzcG0ubW9kZWwzLCBjZXg9MS41LCBsd2Q9NCkKYGBgCgojIyMjIyDjgqzjgqbjgrnjg6Ljg4fjg6sKYGBge3IgdmFyaW8ubW9kZWw0fQpwbG90KHZhcmlvZ3JhbUxpbmUodmdtKHBzaWxsPTIwLCBtb2RlbD0iR2F1IiwgcmFuZ2U9MzUwMDAsIG51Z2dldD01MCksCjcwMDAwLCAxMDApLCB0eXBlPSJsIiwgY2V4PTEuNSwgbHdkPTQsIHlsYWI9InNlbWl2YXJpYW5jZSIsIHhsYWI9ImRpc3RhbmNlIiwKY2V4LmF4aXM9MS4yLCBjZXgubGFiPTEuMiwgeWxpbT1jKDQwLDgwKSkKcG9pbnRzKHNwbS52YXIxJGRpc3QsIHNwbS52YXIxJGdhbW1hLCBjZXg9MS41LCBsd2Q9MikgCiMgc3BtLm1vZGVsNCA8LSB2Z20ocHNpbGw9MjAsIG1vZGVsPSJHYXUiLCByYW5nZT0zNTAwMCwgbnVnZ2V0PTUwKQojIHBsb3Qoc3BtLnZhcjEsIHNwbS5tb2RlbDQsIGNleD0xLjUsIGx3ZD00KQpgYGAKCiMjIyMjIOODiuOCsuODg+ODiOWKueaenOODouODh+ODqwpgYGB7ciB2YXJpby5tb2RlbDV9CnBsb3QodmFyaW9ncmFtTGluZSh2Z20ocHNpbGw9MCwgbW9kZWw9Ik51ZyIsIHJhbmdlPTAsIG51Z2dldD03MCksCjcwMDAwLCAxMDApLCB0eXBlPSJsIiwgY2V4PTEuNSwgbHdkPTQsIHlsYWI9InNlbWl2YXJpYW5jZSIsIHhsYWI9ImRpc3RhbmNlIiwKY2V4LmF4aXM9MS4yLCBjZXgubGFiPTEuMiwgeWxpbT1jKDQwLDgwKSkKcG9pbnRzKHNwbS52YXIxJGRpc3QsIHNwbS52YXIxJGdhbW1hLCBjZXg9MS41LCBsd2Q9MikgCiMgc3BtLm1vZGVsNSA8LSB2Z20ocHNpbGw9MCwgbW9kZWw9Ik51ZyIsIG51Z2dldD03MCkKIyBwbG90KHNwbS52YXIxLCBzcG0ubW9kZWw1LCBjZXg9MS41LCBsd2Q9NCkgCmBgYAoKIyMjIyMgTWF0ZXJu44Oi44OH44OrCmBgYHtyIHZhcmlvLm1vZGVsNn0KcGxvdCh2YXJpb2dyYW1MaW5lKHZnbShwc2lsbD0yNSwgbW9kZWw9Ik1hdCIsIHJhbmdlPTMwMDAwLCBudWdnZXQ9NDUpLAo3MDAwMCwgMTAwKSwgdHlwZT0ibCIsIGNleD0xLjUsIGx3ZD00LCB5bGFiPSJzZW1pdmFyaWFuY2UiLCB4bGFiPSJkaXN0YW5jZSIsCmNleC5heGlzPTEuMiwgY2V4LmxhYj0xLjIsIHlsaW09Yyg0MCw4MCkpCnBvaW50cyhzcG0udmFyMSRkaXN0LCBzcG0udmFyMSRnYW1tYSwgY2V4PTEuNSwgbHdkPTIpIAojIHNwbS5tb2RlbDYgPC0gdmdtKHBzaWxsPTI1LCBtb2RlbD0iTWF0IiwgcmFuZ2U9MzAwMDAsIG51Z2dldD00NSkKIyBwbG90KHNwbS52YXIxLCBzcG0ubW9kZWw2LCBjZXg9MS41LCBsd2Q9NCkKYGBgCgojIyMjIyDmjqjlrprmlrnms5Xjgavjgojjgovjg7TjgqHjg6rjgqrjgrDjg6njg6Djg7vjg6Ljg4fjg6vjga7pgZXjgYQK44O044Kh44Oq44Kq44Kw44Op44Og44O744Oi44OH44Or44Gv5o6o5a6a5pa55rOV44Gr44KI44Gj44Gm5o6o5a6a57WQ5p6c44GM55Ww44Gq44KL5aC05ZCI44GM44GC44KL44CC44GT44GT44Gn44Gv44CB55CD5b2i44Oi44OH44Or44KS5L6L44Gr44CB6YeN44G/5LuY44GN5pyA5bCP5LqM5LmX5rOV44CB6YCa5bi45pyA5bCP5LqM5LmX5rOV44CB5Yi26ZmQ5LuY44GN5pyA5bCk5rOV44Gu77yT44Gk44Gu5o6o5a6a5pa55rOV44Gr44KI44KL5o6o5a6a57WQ5p6c44Gu6YGV44GE44KS56S644Gd44GG44CCCmBgYHtyIHZhcmlvLm1vZGVsN30Kc3BtLm1vZGVsMiA8LSB2Z20ocHNpbGw9MjUsIG1vZGVsPSJTcGgiLCByYW5nZT02MDAwMCwgbnVnZ2V0PTQ1KQpzcG0uZml0IDwtIGZpdC52YXJpb2dyYW0oc3BtLnZhcjEsIHNwbS5tb2RlbDIpCiMg55CD5b2i44Oi44OH44Or77yaV0xTCmZpdC52YXJpb2dyYW0oc3BtLnZhcjEsIHNwbS5tb2RlbDIsIGZpdC5tZXRob2Q9NykKIyDnkIPlvaLjg6Ljg4fjg6vvvJpPTFMKZml0LnZhcmlvZ3JhbShzcG0udmFyMSwgc3BtLm1vZGVsMiwgZml0Lm1ldGhvZD02KQojIOWItumZkOS7mOOBjeacgOWwpOazlQpmaXQudmFyaW9ncmFtLnJlbWwoU1BNMDcqMTAwMH5YK1ksZGF0YT1zcG0udiwgbW9kZWw9dmdtKDI1LCAiU3BoIiwgNjAwMDAsIDQ1KSkKIyBXTFMKcGxvdCh2YXJpb2dyYW1MaW5lKHZnbShwc2lsbD0yNSwgbW9kZWw9IlNwaCIsIHJhbmdlPTYwMDAwLCBudWdnZXQ9NDUpLAo3MDAwMCwgMTAwKSwgdHlwZT0ibCIsIGNleD0xLjUsIGx3ZD00LCB5bGFiPSJzZW1pdmFyaWFuY2UiLCB4bGFiPSJkaXN0YW5jZSIsCmNleC5heGlzPTEuMiwgY2V4LmxhYj0xLjIsIHlsaW09Yyg0MCw4MCkpCiMgT0xTCmxpbmVzKHZhcmlvZ3JhbUxpbmUodmdtKHBzaWxsPTI1LjE1NDk0LCBtb2RlbD0iU3BoIiwgcmFuZ2U9OTM3ODQuNDQsIG51Z2dldD00OC4zNDA1OCksIDEwMDAwMCwgMTAwKSwgY2V4PTEuNSwgbHdkPTQsbHR5PTIpCiMgUkVNTApsaW5lcyh2YXJpb2dyYW1MaW5lKHZnbShwc2lsbD0yOC45NDg1OCwgbW9kZWw9IlNwaCIsIHJhbmdlPTYwMDAwLCBudWdnZXQ9NTEuNTEwMjQpLCAxMDAwMDAsIDEwMCksIGNleD0xLjUsIGx3ZD00LGx0eT0zKQojIOODkOODquOCquOCsOODqeODoApwb2ludHMoc3BtLnZhcjEkZGlzdCwgc3BtLnZhcjEkZ2FtbWEsIGNleD0xLjUsIGx3ZD0yKSAKIyDlh6HkvosKbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZD1jKCJXTFMiLCAiT0xTIiwgIlJFTUwiKSwgbHR5PWMoMSwyLDMpLCBsd2Q9Yyg0LDQsNCksIGNleD0xLjUpCmBgYAoKIyMjIyMg55Ww5pa55oCn44Oi44OH44Oq44Oz44KwCmB2YXJpb2dyYW0oKWDplqLmlbDjga5gYWxwaGFg44KS5oyH5a6a44GZ44KL44Go44CB55Ww5pa55oCnKGFuaXNvdHJvcHkp44KS6ICD5oWu44GX44Gf44O044Kh44Oq44Kq44Kw44Op44Og44O744Oi44OH44Or44KS5o6o5a6a44Gn44GN44KL44CCCmBgYHtyIGFuaXNvMX0Kc3BtLnZhcjIgPC0gdmFyaW9ncmFtKFNQTTA3KjEwMDB+WCtZLCBkYXRhPXNwbS52LCBhbHBoYT0wOjMqOTApCnBsb3Qoc3BtLnZhcjIpCmBgYArjgb7jgZ/jgIFgYW5pc2DjgpLmjIflrprjgZfjgabnlbDmlrnmgKfjg6Ljg4fjg6vjgpLmjqjlrprjgZnjgovjgZPjgajjgoLjgafjgY3jgovjgIIKYGBge3IgYW5pc28yfQpzcG0uYW5pczEgPC0gdmdtKHBzaWxsPTI1LCBtb2RlbD0iR2F1IiwgcmFuZ2U9MzUwMDAsIG51Z2dldD01MCwgYW5pcz1jKDAsIDAuOCkpCnBsb3Qoc3BtLnZhcjIsIHNwbS5hbmlzMSkKYGBgCgojIyMjIOOCr+ODquOCruODs+OCsOOBq+OCiOOCi+WGheaMv+ijnOmWkwrjg7TjgqHjg6rjgqrjgrDjg6njg6Djg7vjg6Ljg4fjg6vjgavmjIfmlbDjg6Ljg4fjg6vjgpLmjqHnlKjjgZfjgIHjgq/jg6rjgq7jg7PjgrDjgavnqbrplpPlhoXmjL/oo5zplpPjgpLpgannlKjjgZfjgojjgYbjgIIKCuOBk+OBk+OBp+OBr+OAgeWNmOe0lOOCr+ODquOCruODs+OCsOOAgemAmuW4uOOCr+ODquOCruODs+OCsOOAgeaZrumBjeOCr+ODquOCruODs+OCsOOAgeODluODreODg+OCr+ODu+OCr+ODquOCruODs+OCsOOBrumBqeeUqOS+i+OCkuekuuOBmeOAggojIyMjIyDljZjntJTjgq/jg6rjgq7jg7PjgrAKYGBge3Iga3JpZ2luZzF9CiNzcG0ubW9kZWwxIDwtIHZnbShwc2lsbD0yNSwgbW9kZWw9IkV4cCIsIHJhbmdlPTI4MDAwLCBudWdnZXQ9NDUpCnNwbS5rcmlnZTEgPC0ga3JpZ2UoU1BNMDcqMTAwMH4xLCBzcG0udiwgbWVzaC5ncmlkLCBtb2RlbD1zcG0ubW9kZWwxLAogICAgICAgICAgICAgICAgICAgYmV0YT1tZWFuKHNwbS52JFNQTTA3KjEwMDApKQpzcHBsb3Qoc3BtLmtyaWdlMVsidmFyMS5wcmVkIl0sIG1haW49IlNpbXBsZSBLcmlnaW5nIFByZWRpY3Rpb24iKQpzcHBsb3Qoc3BtLmtyaWdlMVsidmFyMS52YXIiXSwgbWFpbj0iU2ltcGxlIEtyaWdpbmcgVmFyaWFuY2UiKQpgYGAKCiMjIyMjIOmAmuW4uOOCr+ODquOCruODs+OCsApgYGB7ciBrcmlnaW5nMn0Kc3BtLmtyaWdlMiA8LSBrcmlnZShTUE0wNyoxMDAwfjEsIHNwbS52LCBtZXNoLmdyaWQsIG1vZGVsPXNwbS5tb2RlbDEpCnNwcGxvdChzcG0ua3JpZ2UyWyJ2YXIxLnByZWQiXSwgbWFpbj0iT3JkaW5hcnkgS3JpZ2luZyBQcmVkaWN0aW9uIikKc3BwbG90KHNwbS5rcmlnZTJbInZhcjEudmFyIl0sIG1haW49Ik9yZGluYXJ5IEtyaWdpbmcgVmFyaWFuY2UiKQpgYGAKCiMjIyMjIOaZrumBjeOCr+ODquOCruODs+OCsApgYGB7ciBrcmlnaW5nM30Kc3BtLmtyaWdlMyA8LSBrcmlnZShTUE0wNyoxMDAwflgrWSwgc3BtLnYsIG1lc2guZ3JpZCwgbW9kZWw9c3BtLm1vZGVsMSwgCiAgICAgICAgICAgICAgICAgICAgYmxvY2s9YygyNTAsMjUwKSkKc3BwbG90KHNwbS5rcmlnZTNbInZhcjEucHJlZCJdLCBtYWluPSJVbml2ZXJzYWwgS3JpZ2luZyBQcmVkaWN0aW9uIikKc3BwbG90KHNwbS5rcmlnZTNbInZhcjEudmFyIl0sIG1haW49IlVuaXZlcnNhbCBLcmlnaW5nIFZhcmlhbmNlIikKYGBgCgoKCgoKCg==