Sys.setlocale("LC_ALL","C")
[1] "C"
packages = c(
"dplyr","ggplot2","d3heatmap","googleVis","devtools","plotly", "xgboost",
"magrittr","caTools","ROCR","corrplot", "rpart", "rpart.plot",
"doParallel", "caret", "glmnet", "Matrix", "e1071", "randomForest",
"flexclust", "FactoMineR", "factoextra"
)
existing = as.character(installed.packages()[,1])
for(pkg in packages[!(packages %in% existing)]) install.packages(pkg)
rm(list=ls(all=T))
options(digits=4, scipen=12)
library(dplyr)
library(ggplot2)
library(flexclust)
package 'flexclust' was built under R version 3.4.4Loading required package: grid
Loading required package: modeltools
Loading required package: stats4
Attaching package: 'modeltools'
The following object is masked from 'package:igraph':
clusters
library(FactoMineR)
package 'FactoMineR' was built under R version 3.4.4
library(factoextra)
package 'factoextra' was built under R version 3.4.4Welcome! Related Books: `Practical Guide To Cluster Analysis in R` at https://goo.gl/13EFCZ
A. 集群分析與尺度縮減
A1. 批發交易資料
W = read.csv('data/wholesales.csv')
W$Channel = factor( paste0("Ch",W$Channel) )
W$Region = factor( paste0("Reg",W$Region) )
W[3:8] = lapply(W[3:6], log, base=10)
summary(W)
Channel Region Fresh Milk Grocery Frozen Detergents_Paper
Ch1:298 Reg1: 77 Min. :0.477 Min. :1.74 Min. :0.477 Min. :1.40 Min. :0.477
Ch2:142 Reg2: 47 1st Qu.:3.495 1st Qu.:3.19 1st Qu.:3.333 1st Qu.:2.87 1st Qu.:3.495
Reg3:316 Median :3.930 Median :3.56 Median :3.677 Median :3.18 Median :3.930
Mean :3.792 Mean :3.53 Mean :3.666 Mean :3.17 Mean :3.792
3rd Qu.:4.229 3rd Qu.:3.86 3rd Qu.:4.028 3rd Qu.:3.55 3rd Qu.:4.229
Max. :5.050 Max. :4.87 Max. :4.967 Max. :4.78 Max. :5.050
Delicassen
Min. :1.74
1st Qu.:3.19
Median :3.56
Mean :3.53
3rd Qu.:3.86
Max. :4.87
A2. 兩個區隔變數
hc = W[,3:4] %>% scale %>% dist %>% hclust
plot(hc)
rect.hclust(hc, k=5, border="red")

- 在做集群分析前,先將所有的區隔變數標準化,(使用scale函數),可以讓每個欄位平均值=0,標準差=1,使得在計算距離時不會受差值較大的區隔變數埋沒其他較小的變數。
W$group = cutree(hc, k=5) %>% factor
ggplot(W, aes(x=Fresh, y=Milk, col=group)) +
geom_point(size=3, alpha=0.5) +
theme_light()

A3. 六個區隔變數
hc = W[,3:7] %>% scale %>% dist %>% hclust
plot(hc)
W$group = factor(cutree(hc, k=8))
rect.hclust(hc, k=8, border="red")

library(FactoMineR)
library(factoextra)
fviz_dend(
hc, k=8, show_labels=F, rect=T, rect_fill=T,
labels_track_height=0,
palette="ucscgb", rect_border="ucscgb")

A4. 尺度縮減
Dimension Reduction with PCA (Principle Component Analysis, 主成分分析)
W[,3:8] %>% PCA(graph=F) %>% fviz_pca_biplot(
label="var", col.ind=W$group,
pointshape=19, mean.point=F,
addEllipses=T, ellipse.level=0.7,
ellipse.type = "convex", palette="ucscgb",
repel=T
)

+在低尺度空間看到分群的結果;還有區隔變數(尺度跟尺度)的關係。
1. Cluster Analysis for Movies
主要議題:依類型(Genre)對電影分類
學習重點:
- 集群分析的基本觀念
- 距離矩陣:Distance Matrix
- 層級式集群分析:Hierarchical Cluster Analysis
- 樹狀圖(Dendrogram)的判讀
- 依據樹狀圖決定要分多少群
- 以群組平均值檢視各族群的屬性
1.1 整理資料
M = read.table("data/movieLens.txt", header=FALSE, sep="|",quote="\"")
# Assign column names
colnames(M) = c(
"ID", "Title", "ReleaseDate", "VideoReleaseDate", "IMDB",
"Unknown", "Action", "Adventure", "Animation", "Childrens",
"Comedy", "Crime", "Documentary", "Drama", "Fantasy", "FilmNoir",
"Horror", "Musical", "Mystery", "Romance", "SciFi", "Thriller",
"War", "Western")
# Remove unnecessary variables
M$ID = NULL
M$ReleaseDate = NULL
M$VideoReleaseDate = NULL
M$IMDB = NULL
# Remove duplicates
M = unique(M)
1.2 檢視資料
head(M, 5)
sum(M$Comedy) # 喜劇片
[1] 502
sum(M$Western) # 西部片
[1] 27
sum(M$Romance | M$Drama) # 浪漫劇情片
[1] 863
1.3 距離矩陣
dmx= dist(M[2:20], method="euclidean")
dmx %>% as.matrix %>% dim
[1] 1664 1664
1.4 層級式集群分析
hclust1 = hclust(dmx, method = "ward.D")
1.5 檢視樹狀圖
plot(hclust1)
rect.hclust(hclust1, k=5, border="red")

1.6 切割群組
grp = cutree(hclust1, k = 5)
table(grp)
grp
1 2 3 4 5
824 370 209 196 65
1.7 檢查群組屬性
tapply(M$Action, grp, mean)
1 2 3 4 5
0.28641 0.00000 0.00000 0.06633 0.00000
tapply(M$Romance, grp, mean)
1 2 3 4 5
0.05825 0.00000 0.00000 1.00000 0.00000
1.8 The sapply-split-... Combo:
sapply(split(M[,2:20], grp), colMeans) %>% round(3)
1 2 3 4 5
Unknown 0.002 0 0 0.000 0
Action 0.286 0 0 0.066 0
Adventure 0.161 0 0 0.000 0
Animation 0.051 0 0 0.000 0
Childrens 0.146 0 0 0.000 0
Comedy 0.177 0 1 0.418 1
Crime 0.123 0 0 0.031 0
Documentary 0.061 0 0 0.000 0
Drama 0.238 1 0 0.434 1
Fantasy 0.027 0 0 0.000 0
FilmNoir 0.028 0 0 0.005 0
Horror 0.107 0 0 0.010 0
Musical 0.068 0 0 0.000 0
Mystery 0.073 0 0 0.000 0
Romance 0.058 0 0 1.000 0
SciFi 0.121 0 0 0.000 0
Thriller 0.279 0 0 0.092 0
War 0.086 0 0 0.000 0
Western 0.033 0 0 0.000 0
1.9 資料視覺化
layout(matrix(c(1,2,2), 3, 1))
par(mar=c(2,3,1,1), cex=0.8)
table(grp) %>% barplot(col=3:7, names.arg=paste0("Group-",1:5))
par(mar=c(6,3,2,1))
sapply(split(M[,2:20], grp), colMeans) %>% t %>%
barplot(beside=T, col=3:7, las=2)

【問題討論】
從管理的角度來看,我們為甚麼要分群?
- 集群分析還可以看出某群的一些特性,讓在預測模型之前可以先看出一些端倪。
- 一般來說,將公司面對的客群做適當分群,可以在不同分群做出不同的商業決策。
我們為甚麼要做尺度縮減?
- 可以將許多不同的區隔變數縮減至一個平面,較容易解讀不同分群跟區隔變數的關係。
- 屬性太多的話,在做cluster計算資料點間的距離時,計算會太複雜
- 我們要以資訊量損失最少的角度去做尺度縮減。
我們要如何把集群分析的結果轉化為策略呢?
- 利用集群分析的結果看出不同分群的特性,並針對特性來做出相對策略的回應。
- 還是要搭配決策者的背景知識去做最適當的策略。
2. Flower Image
2.1 整理資料
# Read data
flower = read.csv("data/flower.csv", header=FALSE)
# Change the data type to matrix
flowerMatrix = as.matrix(flower)
dim(flowerMatrix)
[1] 50 50
# Turn matrix into a vector
flowerVector = as.vector(flowerMatrix)
length(flowerVector)
[1] 2500
2.2 距離矩陣
# Compute distances
distance = dist(flowerVector, method = "euclidean")
2.3 層級式集群分析
# Hierarchical clustering
clusterIntensity = hclust(distance, method="ward.D")
2.4 樹狀圖
# Plot the dendrogram
plot(clusterIntensity)
# Select 3 clusters
rect.hclust(clusterIntensity, k = 3, border = "red")

切割群組
flowerClusters = cutree(clusterIntensity, k = 3)
table(flowerClusters)
flowerClusters
1 2 3
1634 272 594
# flowerClusters
族群平均(畫素顏色深淺度)
# Find mean intensity values
tapply(flowerVector, flowerClusters, mean)
1 2 3
0.08574 0.50826 0.93148
圖像比較
# Plot the image and the clusters
dim(flowerClusters) = c(50,50)
par(mfrow=c(1,2), mar=c(2,2,2,2))
# Original image
image(flowerMatrix,axes=FALSE,col=grey(seq(0,1,length=256)),main="Original")
# New image
image(flowerClusters, axes = FALSE, main="3 Cluster")

3. MRI Image
3.1 整理資料
# Read data
healthy = read.csv("data/healthy.csv", header=FALSE)
healthyMatrix = as.matrix(healthy)
dim(healthyMatrix)
[1] 566 646
3.2 畫出圖形
# Plot image
par(mar=c(1,1,1,1))
image(healthyMatrix,axes=FALSE,col=grey(seq(0,1,length=256)))

3.3 距離矩陣
# Compute distances
healthyVector = as.vector(healthyMatrix)
distance = dist(healthyVector, method = "euclidean")
Error: cannot allocate vector of size 498.0 Gb
【Q】 What is the problem?
- 因為矩陣為566*646的大小,若要計算兩點距離來做集群分析,會有太多資料量,而R會負荷不了。
- N=566*646
- Dist的資料數=N(N-1)/2
3.4 KMeans集群分析
# Run k-means
k = 5
set.seed(1)
KMC = kmeans(healthyVector, centers = k, iter.max = 1000)
3.5 檢查分群結果
# View(KMC)
table(KMC$cluster)
1 2 3 4 5
20556 101085 133162 31555 79278
KMC$centers
[,1]
1 0.48177
2 0.10619
3 0.01962
4 0.30943
5 0.18421
3.6 畫出分群結果
# Extract clusters
X = KMC$cluster
# Plot the image with the clusters
dim(X) = c(nrow(healthyMatrix), ncol(healthyMatrix))
# Plot image
par(mar=c(1,1,1,1))
image(X, axes = FALSE, col=rainbow(k))

3.7 讀進、轉換測試圖形
tumor = read.csv("data/tumor.csv", header=FALSE)
tumorMatrix = as.matrix(tumor)
dim(tumorMatrix)
[1] 571 512
tumorVector = as.vector(tumorMatrix)
length(tumorVector)
[1] 292352
3.8 將原圖形之分群規則套用到測試圖形
KMC.kcca = flexclust::as.kcca(KMC, healthyVector) # 建立模型
3.9 圖像比較
# Visualize the clusters
dim(tumorClusters) = c(nrow(tumorMatrix), ncol(tumorMatrix))
par(mfrow=c(1,2), mar=c(1,1,2,1))
image(X, axes = FALSE, col=rainbow(k), main="Healthy")
image(t(tumorClusters)[,571:1], axes = FALSE, col=rainbow(k), main="Tumor")

【學習重點】
- 集群分析在圖像處理的應用
- 單區隔變數的集群分析
- 集群分析模型
【問題討論】
層級式和K-Means集群分析有什麼差異? 它們分別用在什麼狀況?
集群分析模型和普通的集群分析有什麼差異?
- 集群分析是將現有資料做分群,可以看出不同群的特性;
- 但集群分析模型是將這個分群方法建立模型後,可以把額外新的資料投入模型,以同樣的演算法來把新的資料分群。 (利用原先的分群結果放在一個模型,在為第二個資料做分群。)
什麼時候需要建集群分析模型? 集群分析模型的用法?
- 若需要比較變化兩種些微不同資料,利用模型可以將原本相同的部分得到一樣的結果,進而看出差異。
圖像處理和圖像辨識有什麼差異?
- 圖像處理是將原有數值的資料型態,依照集群分析將各數值分類,並依照分群的結果及圖像應有的長寬呈現圖像,呈現方式可依參數設定灰階抑或是彩色。
- 圖像辨識來自於不同數值下所分群的差異,在呈現時會因結果差異而有不同色階,透過這樣的方式可以判別一些特定的圖像,或是時間序列下不同的資料所呈現的圖像可做比較。
- 通常要先做圖像處理才能進一步圖像辨識。
LS0tDQp0aXRsZTogIkFTNi0wIEdyb3VwNCAg6ZuG576k5YiG5p6QIg0KYXV0aG9yOiAi546L5qyjIE0wNjQxMTEwMzkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8YnI+DQoNCmBgYHtyfQ0KU3lzLnNldGxvY2FsZSgiTENfQUxMIiwiQyIpDQpwYWNrYWdlcyA9IGMoDQogICJkcGx5ciIsImdncGxvdDIiLCJkM2hlYXRtYXAiLCJnb29nbGVWaXMiLCJkZXZ0b29scyIsInBsb3RseSIsICJ4Z2Jvb3N0IiwNCiAgIm1hZ3JpdHRyIiwiY2FUb29scyIsIlJPQ1IiLCJjb3JycGxvdCIsICJycGFydCIsICJycGFydC5wbG90IiwNCiAgImRvUGFyYWxsZWwiLCAiY2FyZXQiLCAiZ2xtbmV0IiwgIk1hdHJpeCIsICJlMTA3MSIsICJyYW5kb21Gb3Jlc3QiLA0KICAiZmxleGNsdXN0IiwgIkZhY3RvTWluZVIiLCAiZmFjdG9leHRyYSINCiAgKQ0KZXhpc3RpbmcgPSBhcy5jaGFyYWN0ZXIoaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKQ0KZm9yKHBrZyBpbiBwYWNrYWdlc1shKHBhY2thZ2VzICVpbiUgZXhpc3RpbmcpXSkgaW5zdGFsbC5wYWNrYWdlcyhwa2cpDQpgYGANCg0KYGBge3IgZWNobz1ULCBtZXNzYWdlPUYsIGNhY2hlPUYsIHdhcm5pbmc9Rn0NCnJtKGxpc3Q9bHMoYWxsPVQpKQ0Kb3B0aW9ucyhkaWdpdHM9NCwgc2NpcGVuPTEyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZmxleGNsdXN0KQ0KbGlicmFyeShGYWN0b01pbmVSKQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KYGBgDQoNCi0gLSAtDQoNCiMjIyBBLiDpm4bnvqTliIbmnpDoiIflsLrluqbnuK7muJsNCg0KIyMjIyMgQTEuIOaJueeZvOS6pOaYk+izh+aWmQ0KYGBge3J9DQpXID0gcmVhZC5jc3YoJ2RhdGEvd2hvbGVzYWxlcy5jc3YnKQ0KVyRDaGFubmVsID0gZmFjdG9yKCBwYXN0ZTAoIkNoIixXJENoYW5uZWwpICkNClckUmVnaW9uID0gZmFjdG9yKCBwYXN0ZTAoIlJlZyIsVyRSZWdpb24pICkNCldbMzo4XSA9IGxhcHBseShXWzM6Nl0sIGxvZywgYmFzZT0xMCkNCnN1bW1hcnkoVykNCmBgYA0KDQojIyMjIyBBMi4g5YWp5YCL5Y2A6ZqU6K6K5pW4DQpgYGB7cn0NCmhjID0gV1ssMzo0XSAlPiUgc2NhbGUgJT4lIGRpc3QgJT4lIGhjbHVzdA0KcGxvdChoYykNCnJlY3QuaGNsdXN0KGhjLCBrPTUsIGJvcmRlcj0icmVkIikNCmBgYA0KICsg5Zyo5YGa6ZuG576k5YiG5p6Q5YmN77yM5YWI5bCH5omA5pyJ55qE5Y2A6ZqU6K6K5pW45qiZ5rqW5YyW77yMKOS9v+eUqHNjYWxl5Ye95pW4Ke+8jOWPr+S7peiuk+avj+WAi+ashOS9jeW5s+Wdh+WAvD0wLOaomea6luW3rj0x77yM5L2/5b6X5Zyo6KiI566X6Led6Zui5pmC5LiN5pyD5Y+X5beu5YC86LyD5aSn55qE5Y2A6ZqU6K6K5pW45Z+L5rKS5YW25LuW6LyD5bCP55qE6K6K5pW444CCDQoNCmBgYHtyfQ0KVyRncm91cCA9IGN1dHJlZShoYywgaz01KSAlPiUgZmFjdG9yDQpnZ3Bsb3QoVywgYWVzKHg9RnJlc2gsIHk9TWlsaywgY29sPWdyb3VwKSkgKw0KICBnZW9tX3BvaW50KHNpemU9MywgYWxwaGE9MC41KSArIA0KICB0aGVtZV9saWdodCgpDQpgYGANCg0KIyMjIyMgQTMuIOWFreWAi+WNgOmalOiuiuaVuA0KYGBge3J9DQpoYyA9IFdbLDM6N10gJT4lIHNjYWxlICU+JSBkaXN0ICU+JSBoY2x1c3QNCnBsb3QoaGMpDQpXJGdyb3VwID0gZmFjdG9yKGN1dHJlZShoYywgaz04KSkNCnJlY3QuaGNsdXN0KGhjLCBrPTgsIGJvcmRlcj0icmVkIikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoRmFjdG9NaW5lUikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmZ2aXpfZGVuZCgNCiAgaGMsIGs9OCwgc2hvd19sYWJlbHM9RiwgcmVjdD1ULCByZWN0X2ZpbGw9VCwNCiAgbGFiZWxzX3RyYWNrX2hlaWdodD0wLA0KICBwYWxldHRlPSJ1Y3NjZ2IiLCByZWN0X2JvcmRlcj0idWNzY2diIikNCmBgYA0KDQojIyMjIyBBNC4g5bC65bqm57iu5ribIA0KRGltZW5zaW9uIFJlZHVjdGlvbiB3aXRoIFBDQSAoUHJpbmNpcGxlIENvbXBvbmVudCBBbmFseXNpcywg5Li75oiQ5YiG5YiG5p6QKQ0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OX0NCldbLDM6OF0gJT4lIFBDQShncmFwaD1GKSAlPiUgZnZpel9wY2FfYmlwbG90KA0KICBsYWJlbD0idmFyIiwgY29sLmluZD1XJGdyb3VwLA0KICBwb2ludHNoYXBlPTE5LCBtZWFuLnBvaW50PUYsDQogIGFkZEVsbGlwc2VzPVQsIGVsbGlwc2UubGV2ZWw9MC43LA0KICBlbGxpcHNlLnR5cGUgPSAiY29udmV4IiwgcGFsZXR0ZT0idWNzY2diIiwNCiAgcmVwZWw9VA0KICApDQpgYGANCiAr5Zyo5L2O5bC65bqm56m66ZaT55yL5Yiw5YiG576k55qE57WQ5p6c77yb6YKE5pyJ5Y2A6ZqU6K6K5pW4KOWwuuW6pui3n+WwuuW6pinnmoTpl5zkv4LjgIINCiANCjxicj4NCg0KLSAtIC0NCg0KIyMjIDEuIENsdXN0ZXIgQW5hbHlzaXMgZm9yIE1vdmllcyAgDQoNCioq5Li76KaB6K2w6aGM77ya5L6d6aGe5Z6LKEdlbnJlKeWwjembu+W9seWIhumhnioqDQoNCioq5a2457+S6YeN6bue77yaKioNCg0KKyDpm4bnvqTliIbmnpDnmoTln7rmnKzop4Dlv7UNCisg6Led6Zui55+p6Zmj77yaRGlzdGFuY2UgTWF0cml4DQorIOWxpOe0muW8j+mbhue+pOWIhuaekO+8mkhpZXJhcmNoaWNhbCBDbHVzdGVyIEFuYWx5c2lzDQorIOaoueeLgOWclihEZW5kcm9ncmFtKeeahOWIpOiugA0KKyDkvp3mk5rmqLnni4DlnJbmsbrlrpropoHliIblpJrlsJHnvqQNCisg5Lul576k57WE5bmz5Z2H5YC85qqi6KaW5ZCE5peP576k55qE5bGs5oCnDQoNCjxicj4NCg0KIyMjIyMgMS4xIOaVtOeQhuizh+aWmQ0KYGBge3J9DQpNID0gcmVhZC50YWJsZSgiZGF0YS9tb3ZpZUxlbnMudHh0IiwgaGVhZGVyPUZBTFNFLCBzZXA9InwiLHF1b3RlPSJcIiIpDQoNCiMgQXNzaWduIGNvbHVtbiBuYW1lcw0KY29sbmFtZXMoTSkgPSBjKA0KICAiSUQiLCAiVGl0bGUiLCAiUmVsZWFzZURhdGUiLCAiVmlkZW9SZWxlYXNlRGF0ZSIsICJJTURCIiwgDQogICJVbmtub3duIiwgIkFjdGlvbiIsICJBZHZlbnR1cmUiLCAiQW5pbWF0aW9uIiwgIkNoaWxkcmVucyIsIA0KICAiQ29tZWR5IiwgIkNyaW1lIiwgIkRvY3VtZW50YXJ5IiwgIkRyYW1hIiwgIkZhbnRhc3kiLCAiRmlsbU5vaXIiLCANCiAgIkhvcnJvciIsICJNdXNpY2FsIiwgIk15c3RlcnkiLCAiUm9tYW5jZSIsICJTY2lGaSIsICJUaHJpbGxlciIsDQogICJXYXIiLCAiV2VzdGVybiIpDQoNCiMgUmVtb3ZlIHVubmVjZXNzYXJ5IHZhcmlhYmxlcw0KTSRJRCA9IE5VTEwNCk0kUmVsZWFzZURhdGUgPSBOVUxMDQpNJFZpZGVvUmVsZWFzZURhdGUgPSBOVUxMDQpNJElNREIgPSBOVUxMDQoNCiMgUmVtb3ZlIGR1cGxpY2F0ZXMNCk0gPSB1bmlxdWUoTSkNCmBgYA0KDQojIyMjIyAxLjIg5qqi6KaW6LOH5paZDQpgYGB7cn0NCmhlYWQoTSwgNSkNCmBgYA0KDQpgYGB7cn0NCnN1bShNJENvbWVkeSkgICAgICAgICAgICAgIyDllpzliofniYcNCnN1bShNJFdlc3Rlcm4pICAgICAgICAgICAgIyDopb/pg6jniYcNCnN1bShNJFJvbWFuY2UgfCBNJERyYW1hKSAgIyDmtarmvKvliofmg4XniYcNCmBgYA0KDQojIyMjIyAxLjMg6Led6Zui55+p6ZmjDQpgYGB7cn0NCmRteD0gZGlzdChNWzI6MjBdLCBtZXRob2Q9ImV1Y2xpZGVhbiIpDQpkbXggJT4lIGFzLm1hdHJpeCAlPiUgZGltDQpgYGANCg0KIyMjIyMgMS40IOWxpOe0muW8j+mbhue+pOWIhuaekA0KYGBge3J9DQpoY2x1c3QxID0gaGNsdXN0KGRteCwgbWV0aG9kID0gIndhcmQuRCIpIA0KYGBgDQoNCiMjIyMjIDEuNSDmqqLoppbmqLnni4DlnJYNCmBgYHtyfQ0KcGxvdChoY2x1c3QxKQ0KcmVjdC5oY2x1c3QoaGNsdXN0MSwgaz01LCBib3JkZXI9InJlZCIpDQpgYGANCg0KIyMjIyMgMS42IOWIh+WJsue+pOe1hA0KYGBge3J9DQpncnAgPSBjdXRyZWUoaGNsdXN0MSwgayA9IDUpDQp0YWJsZShncnApDQpgYGANCg0KIyMjIyMgMS43IOaqouafpee+pOe1hOWxrOaApw0KYGBge3J9DQp0YXBwbHkoTSRBY3Rpb24sIGdycCwgbWVhbikNCnRhcHBseShNJFJvbWFuY2UsIGdycCwgbWVhbikNCmBgYA0KDQojIyMjIyAxLjggVGhlIGBzYXBwbHlgLWBzcGxpdGAtYC4uLmAgQ29tYm/vvJoNCmBgYHtyfQ0Kc2FwcGx5KHNwbGl0KE1bLDI6MjBdLCBncnApLCBjb2xNZWFucykgJT4lIHJvdW5kKDMpDQpgYGANCg0KIyMjIyMgMS45IOizh+aWmeimluimuuWMlg0KYGBge3J9DQpsYXlvdXQobWF0cml4KGMoMSwyLDIpLCAzLCAxKSkNCnBhcihtYXI9YygyLDMsMSwxKSwgY2V4PTAuOCkNCnRhYmxlKGdycCkgJT4lIGJhcnBsb3QoY29sPTM6NywgbmFtZXMuYXJnPXBhc3RlMCgiR3JvdXAtIiwxOjUpKQ0KcGFyKG1hcj1jKDYsMywyLDEpKQ0Kc2FwcGx5KHNwbGl0KE1bLDI6MjBdLCBncnApLCBjb2xNZWFucykgJT4lIHQgJT4lIA0KICBiYXJwbG90KGJlc2lkZT1ULCBjb2w9Mzo3LCBsYXM9MikNCg0KYGBgDQoNCiMjIyMjIOOAkOWVj+mhjOiojuirluOAkQ0KDQrlvp7nrqHnkIbnmoTop5LluqbkvobnnIvvvIzmiJHlgJHngrrnlJrpurzopoHliIbnvqTvvJ8gDQoNCisg6ZuG576k5YiG5p6Q6YKE5Y+v5Lul55yL5Ye65p+Q576k55qE5LiA5Lqb54m55oCn77yM6K6T5Zyo6aCQ5ris5qih5Z6L5LmL5YmN5Y+v5Lul5YWI55yL5Ye65LiA5Lqb56uv5YCq44CCDQorIOS4gOiIrOS+huiqqu+8jOWwh+WFrOWPuOmdouWwjeeahOWuoue+pOWBmumBqeeVtuWIhue+pO+8jOWPr+S7peWcqOS4jeWQjOWIhue+pOWBmuWHuuS4jeWQjOeahOWVhualreaxuuetluOAgg0KDQoNCuaIkeWAkeeCuueUmum6vOimgeWBmuWwuuW6pue4rua4m++8nyANCg0KKyDlj6/ku6XlsIfoqLHlpJrkuI3lkIznmoTljYDpmpTorormlbjnuK7muJvoh7PkuIDlgIvlubPpnaLvvIzovIPlrrnmmJPop6PoroDkuI3lkIzliIbnvqTot5/ljYDpmpTorormlbjnmoTpl5zkv4LjgIINCisg5bGs5oCn5aSq5aSa55qE6Kmx77yM5Zyo5YGaY2x1c3RlcuioiOeul+izh+aWmem7numWk+eahOi3nembouaZgu+8jOioiOeul+acg+Wkquikh+mbnA0KKyDmiJHlgJHopoHku6Xos4foqIrph4/mkI3lpLHmnIDlsJHnmoTop5LluqbljrvlgZrlsLrluqbnuK7muJvjgIINCg0KDQrmiJHlgJHopoHlpoLkvZXmiorpm4bnvqTliIbmnpDnmoTntZDmnpzovYnljJbngrrnrZbnlaXlkaLvvJ8gDQoNCisg5Yip55So6ZuG576k5YiG5p6Q55qE57WQ5p6c55yL5Ye65LiN5ZCM5YiG576k55qE54m55oCn77yM5Lim6Yed5bCN54m55oCn5L6G5YGa5Ye655u45bCN562W55Wl55qE5Zue5oeJ44CCDQorIOmChOaYr+imgeaQremFjeaxuuetluiAheeahOiDjOaZr+efpeitmOWOu+WBmuacgOmBqeeVtueahOetlueVpeOAgg0KDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyAyLiBGbG93ZXIgSW1hZ2UNCg0KIyMjIyMgMi4xIOaVtOeQhuizh+aWmQ0KYGBge3J9DQojIFJlYWQgZGF0YQ0KZmxvd2VyID0gcmVhZC5jc3YoImRhdGEvZmxvd2VyLmNzdiIsIGhlYWRlcj1GQUxTRSkNCg0KIyBDaGFuZ2UgdGhlIGRhdGEgdHlwZSB0byBtYXRyaXgNCmZsb3dlck1hdHJpeCA9IGFzLm1hdHJpeChmbG93ZXIpDQpkaW0oZmxvd2VyTWF0cml4KQ0KDQojIFR1cm4gbWF0cml4IGludG8gYSB2ZWN0b3INCmZsb3dlclZlY3RvciA9IGFzLnZlY3RvcihmbG93ZXJNYXRyaXgpDQpsZW5ndGgoZmxvd2VyVmVjdG9yKQ0KYGBgDQoNCiMjIyMjIDIuMiDot53pm6Lnn6npmaMNCmBgYHtyfQ0KIyBDb21wdXRlIGRpc3RhbmNlcw0KZGlzdGFuY2UgPSBkaXN0KGZsb3dlclZlY3RvciwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQpgYGANCg0KIyMjIyMgMi4zIOWxpOe0muW8j+mbhue+pOWIhuaekA0KYGBge3J9DQojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nDQpjbHVzdGVySW50ZW5zaXR5ID0gaGNsdXN0KGRpc3RhbmNlLCBtZXRob2Q9IndhcmQuRCIpDQpgYGANCg0KIyMjIyMgMi40IOaoueeLgOWclg0KYGBge3J9DQojIFBsb3QgdGhlIGRlbmRyb2dyYW0NCnBsb3QoY2x1c3RlckludGVuc2l0eSkNCiMgU2VsZWN0IDMgY2x1c3RlcnMNCnJlY3QuaGNsdXN0KGNsdXN0ZXJJbnRlbnNpdHksIGsgPSAzLCBib3JkZXIgPSAicmVkIikNCmBgYA0KDQojIyMjIyDliIflibLnvqTntYQNCmBgYHtyfQ0KZmxvd2VyQ2x1c3RlcnMgPSBjdXRyZWUoY2x1c3RlckludGVuc2l0eSwgayA9IDMpDQp0YWJsZShmbG93ZXJDbHVzdGVycykNCiMgZmxvd2VyQ2x1c3RlcnMNCmBgYA0KDQojIyMjIyDml4/nvqTlubPlnYco55Wr57Sg6aGP6Imy5rex5re65bqmKQ0KYGBge3J9DQojIEZpbmQgbWVhbiBpbnRlbnNpdHkgdmFsdWVzDQp0YXBwbHkoZmxvd2VyVmVjdG9yLCBmbG93ZXJDbHVzdGVycywgbWVhbikNCmBgYA0KDQojIyMjIyDlnJblg4/mr5TovIMNCmBgYHtyIGZpZy5oZWlnaHQ9My4yLCBmaWcud2lkdGg9Ni40fQ0KIyBQbG90IHRoZSBpbWFnZSBhbmQgdGhlIGNsdXN0ZXJzDQpkaW0oZmxvd2VyQ2x1c3RlcnMpID0gYyg1MCw1MCkNCnBhcihtZnJvdz1jKDEsMiksIG1hcj1jKDIsMiwyLDIpKQ0KDQojIE9yaWdpbmFsIGltYWdlDQppbWFnZShmbG93ZXJNYXRyaXgsYXhlcz1GQUxTRSxjb2w9Z3JleShzZXEoMCwxLGxlbmd0aD0yNTYpKSxtYWluPSJPcmlnaW5hbCIpDQoNCiMgTmV3IGltYWdlDQppbWFnZShmbG93ZXJDbHVzdGVycywgYXhlcyA9IEZBTFNFLCBtYWluPSIzIENsdXN0ZXIiKQ0KYGBgDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyAzLiBNUkkgSW1hZ2UNCg0KIyMjIyMgMy4xIOaVtOeQhuizh+aWmQ0KYGBge3J9DQojIFJlYWQgZGF0YQ0KaGVhbHRoeSA9IHJlYWQuY3N2KCJkYXRhL2hlYWx0aHkuY3N2IiwgaGVhZGVyPUZBTFNFKQ0KaGVhbHRoeU1hdHJpeCA9IGFzLm1hdHJpeChoZWFsdGh5KQ0KZGltKGhlYWx0aHlNYXRyaXgpDQpgYGANCg0KIyMjIyMgMy4yIOeVq+WHuuWcluW9og0KYGBge3IgZmlnLndpZHRoPTIuODMsIGZpZy5oZWlnaHQ9My4yM30NCiMgUGxvdCBpbWFnZQ0KcGFyKG1hcj1jKDEsMSwxLDEpKQ0KaW1hZ2UoaGVhbHRoeU1hdHJpeCxheGVzPUZBTFNFLGNvbD1ncmV5KHNlcSgwLDEsbGVuZ3RoPTI1NikpKQ0KYGBgDQoNCiMjIyMjIDMuMyDot53pm6Lnn6npmaMNCmBgYHtyfQ0KIyBDb21wdXRlIGRpc3RhbmNlcw0KaGVhbHRoeVZlY3RvciA9IGFzLnZlY3RvcihoZWFsdGh5TWF0cml4KQ0KZGlzdGFuY2UgPSBkaXN0KGhlYWx0aHlWZWN0b3IsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KYGBgDQoNCioq44CQUeOAkSoqIFdoYXQgaXMgdGhlIHByb2JsZW0/DQoNCisg5Zug54K655+p6Zmj54K6NTY2KjY0NueahOWkp+Wwj++8jOiLpeimgeioiOeul+WFqem7nui3nembouS+huWBmumbhue+pOWIhuaekO+8jOacg+acieWkquWkmuizh+aWmemHj++8jOiAjFLmnIPosqDojbfkuI3kuobjgIINCisgTj01NjYqNjQ2DQorIERpc3TnmoTos4fmlpnmlbg9TihOLTEpLzINCg0KIyMjIyMgMy40IEtNZWFuc+mbhue+pOWIhuaekA0KYGBge3J9DQojIFJ1biBrLW1lYW5zDQprID0gNQ0Kc2V0LnNlZWQoMSkNCktNQyA9IGttZWFucyhoZWFsdGh5VmVjdG9yLCBjZW50ZXJzID0gaywgaXRlci5tYXggPSAxMDAwKQ0KDQpgYGANCg0KIyMjIyMgMy41IOaqouafpeWIhue+pOe1kOaenA0KYGBge3J9DQojIFZpZXcoS01DKQ0KdGFibGUoS01DJGNsdXN0ZXIpDQpLTUMkY2VudGVycw0KYGBgDQoNCiMjIyMjIDMuNiDnlavlh7rliIbnvqTntZDmnpwNCmBgYHtyIGZpZy53aWR0aD0yLjgzLCBmaWcuaGVpZ2h0PTMuMjN9DQojIEV4dHJhY3QgY2x1c3RlcnMNClggPSBLTUMkY2x1c3Rlcg0KDQojIFBsb3QgdGhlIGltYWdlIHdpdGggdGhlIGNsdXN0ZXJzDQpkaW0oWCkgPSBjKG5yb3coaGVhbHRoeU1hdHJpeCksIG5jb2woaGVhbHRoeU1hdHJpeCkpDQoNCiMgUGxvdCBpbWFnZQ0KcGFyKG1hcj1jKDEsMSwxLDEpKQ0KaW1hZ2UoWCwgYXhlcyA9IEZBTFNFLCBjb2w9cmFpbmJvdyhrKSkNCmBgYA0KDQojIyMjIyAzLjcg6K6A6YCy44CB6L2J5o+b5ris6Kmm5ZyW5b2iDQpgYGB7cn0NCnR1bW9yID0gcmVhZC5jc3YoImRhdGEvdHVtb3IuY3N2IiwgaGVhZGVyPUZBTFNFKQ0KdHVtb3JNYXRyaXggPSBhcy5tYXRyaXgodHVtb3IpDQpkaW0odHVtb3JNYXRyaXgpDQp0dW1vclZlY3RvciA9IGFzLnZlY3Rvcih0dW1vck1hdHJpeCkNCmxlbmd0aCh0dW1vclZlY3RvcikNCmBgYA0KDQojIyMjIyAzLjgg5bCH5Y6f5ZyW5b2i5LmL5YiG576k6KaP5YmH5aWX55So5Yiw5ris6Kmm5ZyW5b2iDQpgYGB7cn0NCiMgQXBwbHkgY2x1c3RlcnMgZnJvbSBiZWZvcmUgdG8gbmV3IGltYWdlLCB1c2luZyB0aGUgZmxleGNsdXN0IHBhY2thZ2UNCmxpYnJhcnkoZmxleGNsdXN0KQ0KdDAgPSBTeXMudGltZSgpDQpLTUMua2NjYSA9IGZsZXhjbHVzdDo6YXMua2NjYShLTUMsIGhlYWx0aHlWZWN0b3IpICAgICAgICAjIOW7uueri+aooeWeiw0KdHVtb3JDbHVzdGVycyA9IHByZWRpY3QoS01DLmtjY2EsIG5ld2RhdGEgPSB0dW1vclZlY3RvcikgIyDpgLLooYzpoJDmuKwo6L2J5o+bKQ0KU3lzLnRpbWUoKSAtIHQwDQpgYGANCg0KIyMjIyMgMy45IOWcluWDj+avlOi8gw0KYGBge3IgZmlnLmhlaWdodD0zLjIsIGZpZy53aWR0aD02fQ0KIyBWaXN1YWxpemUgdGhlIGNsdXN0ZXJzDQpkaW0odHVtb3JDbHVzdGVycykgPSBjKG5yb3codHVtb3JNYXRyaXgpLCBuY29sKHR1bW9yTWF0cml4KSkNCg0KcGFyKG1mcm93PWMoMSwyKSwgbWFyPWMoMSwxLDIsMSkpDQppbWFnZShYLCBheGVzID0gRkFMU0UsIGNvbD1yYWluYm93KGspLCBtYWluPSJIZWFsdGh5IikNCmltYWdlKHQodHVtb3JDbHVzdGVycylbLDU3MToxXSwgYXhlcyA9IEZBTFNFLCBjb2w9cmFpbmJvdyhrKSwgbWFpbj0iVHVtb3IiKQ0KYGBgDQoNCiMjIyMjIOOAkOWtuOe/kumHjem7nuOAkQ0KDQorIOmbhue+pOWIhuaekOWcqOWcluWDj+iZleeQhueahOaHieeUqA0KKyDllq7ljYDpmpTorormlbjnmoTpm4bnvqTliIbmnpANCisg6ZuG576k5YiG5p6Q5qih5Z6LDQoNCiMjIyMjIOOAkOWVj+mhjOiojuirluOAkQ0KDQrlsaTntJrlvI/lkoxLLU1lYW5z6ZuG576k5YiG5p6Q5pyJ5LuA6bq85beu55Ww77yfIOWug+WAkeWIhuWIpeeUqOWcqOS7gOm6vOeLgOazge+8nw0KDQorIOWxpOe0muW8jyhoaWVyYXJjaGljYWwp5Y+v5YiG54K66IGa5ZCI5byP5oiW5YiG6KOC5byPDQogICAgKyDogZrlkIjlvI/mmK/lhYjlsIfmr4/lgIvos4fmlpnpg73oppbngrrkuIDlgItjbHVzdGVy77yM6KiI566XY2x1c3RlcumWk+eahOi3nembouW+jO+8jOaJvuWHuui3nembouacgOi/keeahOWFqeiAheiBmuWQiOaIkOS4gOWAi2NsdXN0ZXLvvIznm7TliLBjbHVzdGVy5pW46YeP56ym5ZCI6Kit5a6a44CCKOeUseS4i+W+gOS4iikNCiAgICArIOWIhuWIl+W8j+WJh+aYr+WFiOWwh+aVtOWAi+izh+aWmeimlueCuuS4gOaVtOWAi2NsdXN0ZXIo55Sx5LiK5b6A5LiLKQ0KICArIOe+pOmWk+i3nembouacieWkmueoruioiOeul+aWueazlShtaW4ubWF4LmF2ZXJhZ2UtbGlua2FnZS4uLikNCiAgKyDlnKjkvb/nlKjkuIrvvIzliKnnlKjpu57lkozpu57nmoTot53pm6LlgZrliIbnvqTvvIzkuI3nlKjlhYjmjIflrprliIZL576kDQogICsg5Y+v5Lul5YGa5Ye6ZGVuZHJvZ3Jhbeeci+WHuue+pOiIh+e+pOS5i+mWk+eahOi3nembouS4puWBmuacgOaBsOeVtueahOWIh+WJsg0KICArIOmAmuW4uOmBqeeUqOaWvOizh+aWmei8g+WwkeeahOaDheazge+8jOizh+aWmemHj+mBjuWkp1LmnIPkuI3pgLLooYzpgYvnrpfjgIINCiAgKyDlj6/ku6XliKnnlKjlj4PmlbjoqK3lrprvvIzlsIdub2lzZeaOkumZpA0KDQorIEstTWVhbnPpm4bnvqTliIbmnpANCiAgKyDlhYjmjIflrprliIbngrpL576k77yM5Lim6Zqo5qmf5bCH6LOH5paZ6bue5oyH5rS+6Iez5p+Q5LiA576k77yM5bCH5q+P5LiA576k55qE5Lit5b+D6bue6KiI566X5Ye65L6G77yM6YeN5paw5bCH6LOH5paZ6bue5YaN5YiG6YWN5Yiw5pyA6L+R55qE5Lit5b+D6bue77yM5LiN5pa35bCH6YCZ5YCL6YGO56iL6YeN6KSH55u05Yiw5rKS5pyJ6YCy5bGV54K65q2iDQogICsg6YCa5bi46LOH5paZ6YeP6YGO5aSn5pmC77yM5pyD5L2/55Soa21lYW7kvoblgZrpm4bnvqTliIbmnpANCiAgK+S9huWQjOethuizh+aWmeWIhuWIpeS9v+eUqOWxpOe0muW8j+WSjGttZWFu55qE57WQ5p6c5Lim5LiN5pyD5LiA6Ie0DQogICsg5a655piT5Y+Xbm9pc2XlvbHpn78o5Zug54K65q+P5YCL6bue5LiA5a6a6YO96KaB5bGs5pa85LiA5YCL576kKQ0KDQoNCumbhue+pOWIhuaekOaooeWei+WSjOaZrumAmueahOmbhue+pOWIhuaekOacieS7gOm6vOW3rueVsO+8nyANCg0KKyDpm4bnvqTliIbmnpDmmK/lsIfnj77mnInos4fmlpnlgZrliIbnvqTvvIzlj6/ku6XnnIvlh7rkuI3lkIznvqTnmoTnibnmgKfvvJsNCisg5L2G6ZuG576k5YiG5p6Q5qih5Z6L5piv5bCH6YCZ5YCL5YiG576k5pa55rOV5bu656uL5qih5Z6L5b6M77yM5Y+v5Lul5oqK6aGN5aSW5paw55qE6LOH5paZ5oqV5YWl5qih5Z6L77yM5Lul5ZCM5qij55qE5ryU566X5rOV5L6G5oqK5paw55qE6LOH5paZ5YiG576k44CCDQogICjliKnnlKjljp/lhYjnmoTliIbnvqTntZDmnpzmlL7lnKjkuIDlgIvmqKHlnovvvIzlnKjngrrnrKzkuozlgIvos4fmlpnlgZrliIbnvqTjgIIpIA0KDQrku4DpurzmmYLlgJnpnIDopoHlu7rpm4bnvqTliIbmnpDmqKHlnovvvJ8g6ZuG576k5YiG5p6Q5qih5Z6L55qE55So5rOV77yfDQoNCisg6Iul6ZyA6KaB5q+U6LyD6K6K5YyW5YWp56iu5Lqb5b6u5LiN5ZCM6LOH5paZ77yM5Yip55So5qih5Z6L5Y+v5Lul5bCH5Y6f5pys55u45ZCM55qE6YOo5YiG5b6X5Yiw5LiA5qij55qE57WQ5p6c77yM6YCy6ICM55yL5Ye65beu55Ww44CCDQoNCg0K5ZyW5YOP6JmV55CG5ZKM5ZyW5YOP6L6o6K2Y5pyJ5LuA6bq85beu55Ww77yfDQoNCisg5ZyW5YOP6JmV55CG5piv5bCH5Y6f5pyJ5pW45YC855qE6LOH5paZ5Z6L5oWL77yM5L6d54Wn6ZuG576k5YiG5p6Q5bCH5ZCE5pW45YC85YiG6aGe77yM5Lim5L6d54Wn5YiG576k55qE57WQ5p6c5Y+K5ZyW5YOP5oeJ5pyJ55qE6ZW35a+s5ZGI54++5ZyW5YOP77yM5ZGI54++5pa55byP5Y+v5L6d5Y+D5pW46Kit5a6a54Gw6ZqO5oqR5oiW5piv5b2p6Imy44CCDQorIOWcluWDj+i+qOitmOS+huiHquaWvOS4jeWQjOaVuOWAvOS4i+aJgOWIhue+pOeahOW3rueVsO+8jOWcqOWRiOePvuaZguacg+WboOe1kOaenOW3rueVsOiAjOacieS4jeWQjOiJsumaju+8jOmAj+mBjumAmeaoo+eahOaWueW8j+WPr+S7peWIpOWIpeS4gOS6m+eJueWumueahOWcluWDj++8jOaIluaYr+aZgumWk+W6j+WIl+S4i+S4jeWQjOeahOizh+aWmeaJgOWRiOePvueahOWcluWDj+WPr+WBmuavlOi8g+OAgg0KKyDpgJrluLjopoHlhYjlgZrlnJblg4/omZXnkIbmiY3og73pgLLkuIDmraXlnJblg4/ovqjorZjjgIINCg0KPGJyPg0KDQotIC0gLQ0KDQo8YnI+PGJyPjxicj48YnI+PGJyPg0KDQo8c3R5bGU+DQouY2FwdGlvbiB7DQogIGNvbG9yOiAjNzc3Ow0KICBtYXJnaW4tdG9wOiAxMHB4Ow0KfQ0KcCBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwcmUgew0KICB3b3JkLWJyZWFrOiBub3JtYWw7DQogIHdvcmQtd3JhcDogbm9ybWFsOw0KICBsaW5lLWhlaWdodDogMTsNCn0NCnByZSBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwLGxpIHsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCi5yew0KICBsaW5lLWhlaWdodDogMS4yOw0KfQ0KDQp0aXRsZXsNCiAgY29sb3I6ICNjYzAwMDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpib2R5ew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDEsaDIsaDMsaDQsaDV7DQogIGNvbG9yOiAjMDA4ODAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDN7DQogIGNvbG9yOiAjYjM2YjAwOw0KICBiYWNrZ3JvdW5kOiAjZmZlMGIzOw0KICBsaW5lLWhlaWdodDogMjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmg1ew0KICBjb2xvcjogIzAwNjAwMDsNCiAgYmFja2dyb3VuZDogI2ZmZmZlMDsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQplbXsNCiAgY29sb3I6ICMwMDAwYzA7DQogIGJhY2tncm91bmQ6ICNmMGYwZjA7DQogIH0NCjwvc3R5bGU+DQoNCg==