Sys.setlocale("LC_ALL","C")
[1] "C/C/C/C/C/en_US.UTF-8"
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)
library(FactoMineR)
library(factoextra)

A. 集群分析與尺度縮減

A1. 批發交易資料
W = read.csv("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    
 Ch1:298   Reg1: 77   Min.   :0.477   Min.   :1.74   Min.   :0.477   Min.   :1.40  
 Ch2:142   Reg2: 47   1st Qu.:3.495   1st Qu.:3.19   1st Qu.:3.333   1st Qu.:2.87  
           Reg3:316   Median :3.930   Median :3.56   Median :3.677   Median :3.18  
                      Mean   :3.792   Mean   :3.53   Mean   :3.666   Mean   :3.17  
                      3rd Qu.:4.229   3rd Qu.:3.86   3rd Qu.:4.028   3rd Qu.:3.55  
                      Max.   :5.050   Max.   :4.87   Max.   :4.967   Max.   :4.78  
 Detergents_Paper   Delicassen  
 Min.   :0.477    Min.   :1.74  
 1st Qu.:3.495    1st Qu.:3.19  
 Median :3.930    Median :3.56  
 Mean   :3.792    Mean   :3.53  
 3rd Qu.:4.229    3rd Qu.:3.86  
 Max.   :5.050    Max.   :4.87  
A2. 兩個區隔變數
hc = W[,3:4] %>% scale %>% dist %>% hclust  
# [,3:4]: fresh, milk
# scale: Normalize 標準化(mean=0, sd=1)
# dist: 距離矩陣
# hclust: 集群分析(h)
# 回傳一個 Hierarchical Clustering 的物件
plot(hc)
rect.hclust(hc, k=5, border="red")

# k 是測試出來的,可以多方測試看切得好不好再來決定~
# 找切下去「垂直距離較長的」通常比較好~
# 沒有標準知道切多少比較好
W$group = cutree(hc, k=5) %>% factor
# cutree: 分群
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
# [,3:7]: Fresh,......, Detergent_Paper
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)對電影分類

學習重點:


1.1 整理資料
M = read.table("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
# |: or
1.3 距離矩陣
# 沒有scale: 因為結果都是0、1,沒有必要做 normalize
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)
# cutree 出來的結果會是「整數向量」
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 
# 統計族群中【Action群】的比例
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
# [,2:20]: 去掉片名的19個標籤
# sapply(集合, ): 把第一個list中的每個項目都去呼叫後面的 grp
# (split(M[]), grp): 把一個資料框根據 grp 切開,分成5個(k分的個數)
# colMean: 同 tapply(M$Action, grp, mean),共做了 5 次
# round(3): 小數點後三位
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)

【問題討論】

從管理的角度來看,我們為甚麼要分群?

  • 管理面:藉由分群的方式,讓相同類型或屬性的人員放在一起,以便於管理者進行管理
  • 成本面:當把相類似的群體放咋一起時,單一管理者可以同時管理最多人,以縮減人事成本
  • 執行面:藉由先將類似預測對象分群針對某一群人進行分群,結果通常會變得更加準確

我們為甚麼要做尺度縮減?

  • 將資料投射在變數平面,讓人們得以辨識「不同群體」,再將n-dimension的資料壓縮到2D平面,使得會壓在一個資料損失最少的角度,讓2D資料呈現量最大。

我們要如何把集群分析的結果轉化為策略呢?

  • 藉由集群分析進行尺度縮減之後,可以將n-D資料壓縮2在資料呈現量最大的2D平面上。
  • 壓縮後的2D圖型,可以可以更加視覺化的讓我們瞭解資料彼此之間的關聯性。
  • 藉由瞭解資料彼此間的關聯性,可以藉由資料彼此間的關係,或是資料與時間的關聯性,來針對使用者投放個人化的行銷策略,以增加銷售額、毛利、品牌印象,得以讓策略



2. Flower Image

2.1 整理資料
# Read data
flower = read.csv("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("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: vector memory exhausted (limit reached?)

【Q】 What is the problem?

  • 圖形的pixel多,矩陣乘出來的數值會變得非常非常大,因而導致出現error的訊息以防止電腦當機。
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("tumor.csv", header=FALSE)
tumorMatrix = as.matrix(tumor)
dim(tumorMatrix)
[1] 571 512
tumorVector = as.vector(tumorMatrix)
length(tumorVector)
[1] 292352
3.8 將原圖形之分群規則套用到測試圖形
# Apply clusters from before to new image, using the flexclust package
library(flexclust)
t0 = Sys.time()
KMC.kcca = flexclust::as.kcca(KMC, healthyVector)        # 建立模型
Found more than one class "kcca" in cache; using the first, from namespace 'flexclust'
Also defined by 'kernlab'
Found more than one class "kcca" in cache; using the first, from namespace 'flexclust'
Also defined by 'kernlab'
tumorClusters = predict(KMC.kcca, newdata = tumorVector) # 進行預測(轉換)
Found more than one class "kcca" in cache; using the first, from namespace 'flexclust'
Also defined by 'kernlab'
Sys.time() - t0
Time difference of 34.51 secs
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集群分析有什麼差異? 它們分別用在什麼狀況?

  • 層級式:
    • 從單筆資料開始,不用一開始就決定要分成多少群,最終所有的clusters都會成為一個cluster(通常適用於不要太大的資料集)
  • K-Means:
    • 事先定義好切成k組,最終即會出現所需分組組數的分群結果即便(資料量再大都能輕易做出來 )

集群分析模型和普通的集群分析有什麼差異?

  • 集群分析模型:
  • 集群分析模型與普通集群分析模型不同,集群分析模型可以將原始資料也分成Training Data與Testing Data來建立模型,再由建立出來的集群分析模型將更多未知的資料為進去,進而能夠針對不同群族進行結果之預測

  • 普通集群分析:
    • 目標由資料分群結果而定,而非來自於training data預測結果之模型,藉由兩類型(層級式、K-means)的建模方式,可以將資料依照階層將所有資料進行分層繪製出dendorgram,或是依照欲分組數k得出分類結果


什麼時候需要建集群分析模型? 集群分析模型的用法?

  • 當資料當中族群組內差異甚大,但是這其中又有一群一群的關係,使分組後組內差異小的情況產生時,便適合使用建置集群分析模型,若是與其他監督式學習方法一同執行,所做出預測之準確度會遠比沒有建集群分析模型高出許多,進而也能幫助做出決策分析。

圖像處理和圖像辨識有什麼差異?

  • 圖像處理:
    • 圖像處理是較基礎的圖像製作,藉由將圖像們細分成不同的pixel後組合而成,可以讓所有的圖像都能被分割成不同的小單位,且組成的每一單位佔原圖像的比例也攸關到圖像的解析度。(每一張圖像的內部處理)
  • 圖像辨識:
    • 圖像辨識是機器學習所採用的概念,藉由將許許多多類似的圖片丟進辨識的工具後,能夠辨別出形狀、顏色等細節的差異,而若是丟足夠多的圖像進去機器的話,其辨識準確度會非常高。(多張圖像的疊合對比處理)








LS0tCnRpdGxlOiAiQVM2LTAg6ZuG576k5YiG5p6QIgphdXRob3I6ICJHUk9VUDXigJTigJTmlr3ph4flvaPjgIHpmbPmgKHlronjgIHmpYrlh7HlgKvjgIHllJDmgJ3nkKrjgIHlh4zlgYnoqqAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxicj4KCmBgYHtyfQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCJDIikKcGFja2FnZXMgPSBjKAogICJkcGx5ciIsImdncGxvdDIiLCJkM2hlYXRtYXAiLCJnb29nbGVWaXMiLCJkZXZ0b29scyIsInBsb3RseSIsICJ4Z2Jvb3N0IiwKICAibWFncml0dHIiLCJjYVRvb2xzIiwiUk9DUiIsImNvcnJwbG90IiwgInJwYXJ0IiwgInJwYXJ0LnBsb3QiLAogICJkb1BhcmFsbGVsIiwgImNhcmV0IiwgImdsbW5ldCIsICJNYXRyaXgiLCAiZTEwNzEiLCAicmFuZG9tRm9yZXN0IiwKICAiZmxleGNsdXN0IiwgIkZhY3RvTWluZVIiLCAiZmFjdG9leHRyYSIKICApCmV4aXN0aW5nID0gYXMuY2hhcmFjdGVyKGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSkKZm9yKHBrZyBpbiBwYWNrYWdlc1shKHBhY2thZ2VzICVpbiUgZXhpc3RpbmcpXSkgaW5zdGFsbC5wYWNrYWdlcyhwa2cpCmBgYAoKYGBge3IgZWNobz1ULCBtZXNzYWdlPUYsIGNhY2hlPUYsIHdhcm5pbmc9Rn0Kcm0obGlzdD1scyhhbGw9VCkpCm9wdGlvbnMoZGlnaXRzPTQsIHNjaXBlbj0xMikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGZsZXhjbHVzdCkKbGlicmFyeShGYWN0b01pbmVSKQpsaWJyYXJ5KGZhY3RvZXh0cmEpCmBgYAoKLSAtIC0KCiMjIyBBLiDpm4bnvqTliIbmnpDoiIflsLrluqbnuK7muJsKCiMjIyMjIEExLiDmibnnmbzkuqTmmJPos4fmlpkKYGBge3J9ClcgPSByZWFkLmNzdigid2hvbGVzYWxlcy5jc3YiKQpXJENoYW5uZWwgPSBmYWN0b3IoIHBhc3RlMCgiQ2giLFckQ2hhbm5lbCkgKQpXJFJlZ2lvbiA9IGZhY3RvciggcGFzdGUwKCJSZWciLFckUmVnaW9uKSApCldbMzo4XSA9IGxhcHBseShXWzM6Nl0sIGxvZywgYmFzZT0xMCkKc3VtbWFyeShXKQpgYGAKCiMjIyMjIEEyLiDlhanlgIvljYDpmpTorormlbgKYGBge3J9CmhjID0gV1ssMzo0XSAlPiUgc2NhbGUgJT4lIGRpc3QgJT4lIGhjbHVzdCAgCiMgWywzOjRdOiBmcmVzaCwgbWlsawojIHNjYWxlOiBOb3JtYWxpemUg5qiZ5rqW5YyW77yIbWVhbj0wLCBzZD0xKQojIGRpc3Q6IOi3nembouefqemZowojIGhjbHVzdDog6ZuG576k5YiG5p6Q77yIaO+8iQoKIyDlm57lgrPkuIDlgIsgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcg55qE54mp5Lu2CnBsb3QoaGMpCnJlY3QuaGNsdXN0KGhjLCBrPTUsIGJvcmRlcj0icmVkIikKIyBrIOaYr+a4rOippuWHuuS+hueahO+8jOWPr+S7peWkmuaWuea4rOippueci+WIh+W+l+WlveS4jeWlveWGjeS+huaxuuWumu+9ngojIOaJvuWIh+S4i+WOu+OAjOWeguebtOi3nemboui8g+mVt+eahOOAjemAmuW4uOavlOi8g+Wlve+9ngojIOaykuacieaomea6luefpemBk+WIh+WkmuWwkeavlOi8g+WlvQpgYGAKCmBgYHtyfQpXJGdyb3VwID0gY3V0cmVlKGhjLCBrPTUpICU+JSBmYWN0b3IKIyBjdXRyZWU6IOWIhue+pApnZ3Bsb3QoVywgYWVzKHg9RnJlc2gsIHk9TWlsaywgY29sPWdyb3VwKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zLCBhbHBoYT0wLjUpICsgCiAgdGhlbWVfbGlnaHQoKQpgYGAKCiMjIyMjIEEzLiDlha3lgIvljYDpmpTorormlbgKYGBge3J9CmhjID0gV1ssMzo3XSAlPiUgc2NhbGUgJT4lIGRpc3QgJT4lIGhjbHVzdAojIFssMzo3XTogRnJlc2gsLi4uLi4uLCBEZXRlcmdlbnRfUGFwZXIKcGxvdChoYykKVyRncm91cCA9IGZhY3RvcihjdXRyZWUoaGMsIGs9OCkpCnJlY3QuaGNsdXN0KGhjLCBrPTgsIGJvcmRlcj0icmVkIikKYGBgCgpgYGB7cn0KbGlicmFyeShGYWN0b01pbmVSKQpsaWJyYXJ5KGZhY3RvZXh0cmEpCmZ2aXpfZGVuZCgKICBoYywgaz04LCBzaG93X2xhYmVscz1GLCByZWN0PVQsIHJlY3RfZmlsbD1ULAogIGxhYmVsc190cmFja19oZWlnaHQ9MCwKICBwYWxldHRlPSJ1Y3NjZ2IiLCByZWN0X2JvcmRlcj0idWNzY2diIikKYGBgCgojIyMjIyBBNC4g5bC65bqm57iu5ribIApEaW1lbnNpb24gUmVkdWN0aW9uIHdpdGggUENBIChQcmluY2lwbGUgQ29tcG9uZW50IEFuYWx5c2lzLCDkuLvmiJDliIbliIbmnpApCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9CldbLDM6OF0gJT4lIFBDQShncmFwaD1GKSAlPiUgZnZpel9wY2FfYmlwbG90KAogIGxhYmVsPSJ2YXIiLCBjb2wuaW5kPVckZ3JvdXAsCiAgcG9pbnRzaGFwZT0xOSwgbWVhbi5wb2ludD1GLAogIGFkZEVsbGlwc2VzPVQsIGVsbGlwc2UubGV2ZWw9MC43LAogIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLCBwYWxldHRlPSJ1Y3NjZ2IiLAogIHJlcGVsPVQKICApCmBgYAo8YnI+CgotIC0gLQoKIyMjIDEuIENsdXN0ZXIgQW5hbHlzaXMgZm9yIE1vdmllcyAgCgoqKuS4u+imgeitsOmhjO+8muS+nemhnuWeiyhHZW5yZSnlsI3pm7vlvbHliIbpoZ4qKgoKKirlrbjnv5Lph43pu57vvJoqKgoKKyDpm4bnvqTliIbmnpDnmoTln7rmnKzop4Dlv7UKKyDot53pm6Lnn6npmaPvvJpEaXN0YW5jZSBNYXRyaXgKKyDlsaTntJrlvI/pm4bnvqTliIbmnpDvvJpIaWVyYXJjaGljYWwgQ2x1c3RlciBBbmFseXNpcworIOaoueeLgOWclihEZW5kcm9ncmFtKeeahOWIpOiugAorIOS+neaTmuaoueeLgOWcluaxuuWumuimgeWIhuWkmuWwkee+pAorIOS7pee+pOe1hOW5s+Wdh+WAvOaqouimluWQhOaXj+e+pOeahOWxrOaApwoKPGJyPgoKIyMjIyMgMS4xIOaVtOeQhuizh+aWmQpgYGB7cn0KTSA9IHJlYWQudGFibGUoIm1vdmllTGVucy50eHQiLCBoZWFkZXI9RkFMU0UsIHNlcD0ifCIscXVvdGU9IlwiIikKCiMgQXNzaWduIGNvbHVtbiBuYW1lcwpjb2xuYW1lcyhNKSA9IGMoCiAgIklEIiwgIlRpdGxlIiwgIlJlbGVhc2VEYXRlIiwgIlZpZGVvUmVsZWFzZURhdGUiLCAiSU1EQiIsIAogICJVbmtub3duIiwgIkFjdGlvbiIsICJBZHZlbnR1cmUiLCAiQW5pbWF0aW9uIiwgIkNoaWxkcmVucyIsIAogICJDb21lZHkiLCAiQ3JpbWUiLCAiRG9jdW1lbnRhcnkiLCAiRHJhbWEiLCAiRmFudGFzeSIsICJGaWxtTm9pciIsIAogICJIb3Jyb3IiLCAiTXVzaWNhbCIsICJNeXN0ZXJ5IiwgIlJvbWFuY2UiLCAiU2NpRmkiLCAiVGhyaWxsZXIiLAogICJXYXIiLCAiV2VzdGVybiIpCgojIFJlbW92ZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMKTSRJRCA9IE5VTEwKTSRSZWxlYXNlRGF0ZSA9IE5VTEwKTSRWaWRlb1JlbGVhc2VEYXRlID0gTlVMTApNJElNREIgPSBOVUxMCgojIFJlbW92ZSBkdXBsaWNhdGVzCk0gPSB1bmlxdWUoTSkKYGBgCgojIyMjIyAxLjIg5qqi6KaW6LOH5paZCmBgYHtyfQpoZWFkKE0sIDUpCmBgYAoKYGBge3J9CnN1bShNJENvbWVkeSkgICAgICAgICAgICAgIyDllpzliofniYcKc3VtKE0kV2VzdGVybikgICAgICAgICAgICAjIOilv+mDqOeJhwpzdW0oTSRSb21hbmNlIHwgTSREcmFtYSkgICMg5rWq5ryr5YqH5oOF54mHCiMg772c77yaIG9yCmBgYAoKIyMjIyMgMS4zIOi3nembouefqemZowpgYGB7cn0KIyDmspLmnIlzY2FsZTog5Zug54K657WQ5p6c6YO95pivMOOAgTHvvIzmspLmnInlv4XopoHlgZogbm9ybWFsaXplCmRteD0gZGlzdChNWzI6MjBdLCBtZXRob2Q9ImV1Y2xpZGVhbiIpCmRteCAlPiUgYXMubWF0cml4ICU+JSBkaW0KYGBgCgojIyMjIyAxLjQg5bGk57Sa5byP6ZuG576k5YiG5p6QCmBgYHtyfQpoY2x1c3QxID0gaGNsdXN0KGRteCwgbWV0aG9kID0gIndhcmQuRCIpIApgYGAKCiMjIyMjIDEuNSDmqqLoppbmqLnni4DlnJYKYGBge3J9CnBsb3QoaGNsdXN0MSkKcmVjdC5oY2x1c3QoaGNsdXN0MSwgaz01LCBib3JkZXI9InJlZCIpCmBgYAoKIyMjIyMgMS42IOWIh+WJsue+pOe1hApgYGB7cn0KZ3JwID0gY3V0cmVlKGhjbHVzdDEsIGsgPSA1KQojIGN1dHJlZSDlh7rkvobnmoTntZDmnpzmnIPmmK/jgIzmlbTmlbjlkJHph4/jgI0KdGFibGUoZ3JwKQpgYGAKCiMjIyMjIDEuNyDmqqLmn6XnvqTntYTlsazmgKcKYGBge3J9CiMg5YGa5a6M44CM6ZuG576k5YiG5p6Q44CN5b6M5pyD5oOz5YGa55qE56ys5LiA5Lu25LqLCnRhcHBseShNJEFjdGlvbiwgZ3JwLCBtZWFuKQojIOe1seioiOaXj+e+pOS4reOAkEFjdGlvbue+pOOAkeeahOavlOS+iwp0YXBwbHkoTSRSb21hbmNlLCBncnAsIG1lYW4pCmBgYAoKIyMjIyMgMS44IFRoZSBgc2FwcGx5YC1gc3BsaXRgLWAuLi5gIENvbWJv77yaCmBgYHtyfQpzYXBwbHkoc3BsaXQoTVssMjoyMF0sIGdycCksIGNvbE1lYW5zKSAlPiUgcm91bmQoMykKIyBbLDI6MjBdOiDljrvmjonniYflkI3nmoQxOeWAi+aomeexpAojIHNhcHBsee+8iOmbhuWQiCwg77yJOiDmiornrKzkuIDlgItsaXN05Lit55qE5q+P5YCL6aCF55uu6YO95Y675ZG85Y+r5b6M6Z2i55qEIGdycAojIChzcGxpdChNW10pLCBncnApOiDmiorkuIDlgIvos4fmlpnmoYbmoLnmk5ogZ3JwIOWIh+mWi++8jOWIhuaIkDXlgIvvvIhr5YiG55qE5YCL5pW477yJCiMgY29sTWVhbjog5ZCMIHRhcHBseShNJEFjdGlvbiwgZ3JwLCBtZWFuKe+8jOWFseWBmuS6hiA1IOasoQojIHJvdW5kKDMpOiDlsI/mlbjpu57lvozkuInkvY0KYGBgCgojIyMjIyAxLjkg6LOH5paZ6KaW6Ka65YyWCmBgYHtyfQpsYXlvdXQobWF0cml4KGMoMSwyLDIpLCAzLCAxKSkKcGFyKG1hcj1jKDIsMywxLDEpLCBjZXg9MC44KQp0YWJsZShncnApICU+JSBiYXJwbG90KGNvbD0zOjcsIG5hbWVzLmFyZz1wYXN0ZTAoIkdyb3VwLSIsMTo1KSkKcGFyKG1hcj1jKDYsMywyLDEpKQpzYXBwbHkoc3BsaXQoTVssMjoyMF0sIGdycCksIGNvbE1lYW5zKSAlPiUgdCAlPiUgCiAgYmFycGxvdChiZXNpZGU9VCwgY29sPTM6NywgbGFzPTIpCgpgYGAKCiMjIyMjIOOAkOWVj+mhjOiojuirluOAkQoK5b6e566h55CG55qE6KeS5bqm5L6G55yL77yM5oiR5YCR54K655Sa6bq86KaB5YiG576k77yfIAoKKyDnrqHnkIbpnaLvvJrol4nnlLHliIbnvqTnmoTmlrnlvI/vvIzorpPnm7jlkIzpoZ7lnovmiJblsazmgKfnmoTkurrlk6HmlL7lnKjkuIDotbfvvIzku6Xkvr/mlrznrqHnkIbogIXpgLLooYznrqHnkIYKKyDmiJDmnKzpnaLvvJrnlbbmiornm7jpoZ7kvLznmoTnvqTpq5TmlL7lkovkuIDotbfmmYLvvIzllq7kuIDnrqHnkIbogIXlj6/ku6XlkIzmmYLnrqHnkIbmnIDlpJrkurrvvIzku6XnuK7muJvkurrkuovmiJDmnKwKKyDln7fooYzpnaLvvJrol4nnlLHlhYjlsIfpoZ7kvLzpoJDmuKzlsI3osaHliIbnvqTph53lsI3mn5DkuIDnvqTkurrpgLLooYzliIbnvqTvvIzntZDmnpzpgJrluLjmnIPororlvpfmm7TliqDmupbnoroKCgoK5oiR5YCR54K655Sa6bq86KaB5YGa5bC65bqm57iu5rib77yfIAoKKyDlsIfos4fmlpnmipXlsITlnKjorormlbjlubPpnaLvvIzorpPkurrlgJHlvpfku6XovqjorZjjgIzkuI3lkIznvqTpq5TjgI3vvIzlho3lsIduLWRpbWVuc2lvbueahOizh+aWmeWjk+e4ruWIsDJE5bmz6Z2i77yM5L2/5b6X5pyD5aOT5Zyo5LiA5YCL6LOH5paZ5pCN5aSx5pyA5bCR55qE6KeS5bqm77yM6K6TMkTos4fmlpnlkYjnj77ph4/mnIDlpKfjgIIKCgrmiJHlgJHopoHlpoLkvZXmiorpm4bnvqTliIbmnpDnmoTntZDmnpzovYnljJbngrrnrZbnlaXlkaLvvJ8gCgorIOiXieeUsembhue+pOWIhuaekOmAsuihjOWwuuW6pue4rua4m+S5i+W+jO+8jOWPr+S7peWwh24tROizh+aWmeWjk+e4rjLlnKjos4fmlpnlkYjnj77ph4/mnIDlpKfnmoQyROW5s+mdouS4iuOAggorIOWjk+e4ruW+jOeahDJE5ZyW5Z6L77yM5Y+v5Lul5Y+v5Lul5pu05Yqg6KaW6Ka65YyW55qE6K6T5oiR5YCR556t6Kej6LOH5paZ5b285q2k5LmL6ZaT55qE6Zec6IGv5oCn44CCCisg6JeJ55Sx556t6Kej6LOH5paZ5b285q2k6ZaT55qE6Zec6IGv5oCn77yM5Y+v5Lul6JeJ55Sx6LOH5paZ5b285q2k6ZaT55qE6Zec5L+C77yM5oiW5piv6LOH5paZ6IiH5pmC6ZaT55qE6Zec6IGv5oCn77yM5L6G6Yed5bCN5L2/55So6ICF5oqV5pS+5YCL5Lq65YyW55qE6KGM6Yq3562W55Wl77yM5Lul5aKe5Yqg6Yq35ZSu6aGN44CB5q+b5Yip44CB5ZOB54mM5Y2w6LGh77yM5b6X5Lul6K6T562W55WlCgo8YnI+CgotIC0gLQoKIyMjIDIuIEZsb3dlciBJbWFnZQoKIyMjIyMgMi4xIOaVtOeQhuizh+aWmQpgYGB7cn0KIyBSZWFkIGRhdGEKZmxvd2VyID0gcmVhZC5jc3YoImZsb3dlci5jc3YiLCBoZWFkZXI9RkFMU0UpCgojIENoYW5nZSB0aGUgZGF0YSB0eXBlIHRvIG1hdHJpeApmbG93ZXJNYXRyaXggPSBhcy5tYXRyaXgoZmxvd2VyKQpkaW0oZmxvd2VyTWF0cml4KQoKIyBUdXJuIG1hdHJpeCBpbnRvIGEgdmVjdG9yCmZsb3dlclZlY3RvciA9IGFzLnZlY3RvcihmbG93ZXJNYXRyaXgpCmxlbmd0aChmbG93ZXJWZWN0b3IpCmBgYAoKIyMjIyMgMi4yIOi3nembouefqemZowpgYGB7cn0KIyBDb21wdXRlIGRpc3RhbmNlcwpkaXN0YW5jZSA9IGRpc3QoZmxvd2VyVmVjdG9yLCBtZXRob2QgPSAiZXVjbGlkZWFuIikKYGBgCgojIyMjIyAyLjMg5bGk57Sa5byP6ZuG576k5YiG5p6QCmBgYHtyfQojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCmNsdXN0ZXJJbnRlbnNpdHkgPSBoY2x1c3QoZGlzdGFuY2UsIG1ldGhvZD0id2FyZC5EIikKYGBgCgojIyMjIyAyLjQg5qi554uA5ZyWCmBgYHtyfQojIFBsb3QgdGhlIGRlbmRyb2dyYW0KcGxvdChjbHVzdGVySW50ZW5zaXR5KQojIFNlbGVjdCAzIGNsdXN0ZXJzCnJlY3QuaGNsdXN0KGNsdXN0ZXJJbnRlbnNpdHksIGsgPSAzLCBib3JkZXIgPSAicmVkIikKYGBgCgojIyMjIyDliIflibLnvqTntYQKYGBge3J9CmZsb3dlckNsdXN0ZXJzID0gY3V0cmVlKGNsdXN0ZXJJbnRlbnNpdHksIGsgPSAzKQp0YWJsZShmbG93ZXJDbHVzdGVycykKIyBmbG93ZXJDbHVzdGVycwpgYGAKCiMjIyMjIOaXj+e+pOW5s+WdhyjnlavntKDpoY/oibLmt7Hmt7rluqYpCmBgYHtyfQojIEZpbmQgbWVhbiBpbnRlbnNpdHkgdmFsdWVzCnRhcHBseShmbG93ZXJWZWN0b3IsIGZsb3dlckNsdXN0ZXJzLCBtZWFuKQpgYGAKCiMjIyMjIOWcluWDj+avlOi8gwpgYGB7ciBmaWcuaGVpZ2h0PTMuMiwgZmlnLndpZHRoPTYuNH0KIyBQbG90IHRoZSBpbWFnZSBhbmQgdGhlIGNsdXN0ZXJzCmRpbShmbG93ZXJDbHVzdGVycykgPSBjKDUwLDUwKQpwYXIobWZyb3c9YygxLDIpLCBtYXI9YygyLDIsMiwyKSkKCiMgT3JpZ2luYWwgaW1hZ2UKaW1hZ2UoZmxvd2VyTWF0cml4LGF4ZXM9RkFMU0UsY29sPWdyZXkoc2VxKDAsMSxsZW5ndGg9MjU2KSksbWFpbj0iT3JpZ2luYWwiKQoKIyBOZXcgaW1hZ2UKaW1hZ2UoZmxvd2VyQ2x1c3RlcnMsIGF4ZXMgPSBGQUxTRSwgbWFpbj0iMyBDbHVzdGVyIikKYGBgCjxicj4KCi0gLSAtCgojIyMgMy4gTVJJIEltYWdlCgojIyMjIyAzLjEg5pW055CG6LOH5paZCmBgYHtyfQojIFJlYWQgZGF0YQpoZWFsdGh5ID0gcmVhZC5jc3YoImhlYWx0aHkuY3N2IiwgaGVhZGVyPUZBTFNFKQpoZWFsdGh5TWF0cml4ID0gYXMubWF0cml4KGhlYWx0aHkpCmRpbShoZWFsdGh5TWF0cml4KQpgYGAKCiMjIyMjIDMuMiDnlavlh7rlnJblvaIKYGBge3IgZmlnLndpZHRoPTIuODMsIGZpZy5oZWlnaHQ9My4yM30KIyBQbG90IGltYWdlCnBhcihtYXI9YygxLDEsMSwxKSkKaW1hZ2UoaGVhbHRoeU1hdHJpeCxheGVzPUZBTFNFLGNvbD1ncmV5KHNlcSgwLDEsbGVuZ3RoPTI1NikpKQpgYGAKCiMjIyMjIDMuMyDot53pm6Lnn6npmaMKYGBge3J9CiMgQ29tcHV0ZSBkaXN0YW5jZXMKaGVhbHRoeVZlY3RvciA9IGFzLnZlY3RvcihoZWFsdGh5TWF0cml4KQpkaXN0YW5jZSA9IGRpc3QoaGVhbHRoeVZlY3RvciwgbWV0aG9kID0gImV1Y2xpZGVhbiIpCmBgYAoKKirjgJBR44CRKiogV2hhdCBpcyB0aGUgcHJvYmxlbT8KCisg5ZyW5b2i55qEcGl4ZWzlpJrvvIznn6npmaPkuZjlh7rkvobnmoTmlbjlgLzmnIPororlvpfpnZ7luLjpnZ7luLjlpKfvvIzlm6DogIzlsI7oh7Tlh7rnj75lcnJvcueahOioiuaBr+S7pemYsuatoumbu+iFpueVtuapn+OAggoKIyMjIyMgMy40IEtNZWFuc+mbhue+pOWIhuaekApgYGB7cn0KIyBSdW4gay1tZWFucwprID0gNQpzZXQuc2VlZCgxKQpLTUMgPSBrbWVhbnMoaGVhbHRoeVZlY3RvciwgY2VudGVycyA9IGssIGl0ZXIubWF4ID0gMTAwMCkKYGBgCgojIyMjIyAzLjUg5qqi5p+l5YiG576k57WQ5p6cCmBgYHtyfQojIFZpZXcoS01DKQp0YWJsZShLTUMkY2x1c3RlcikKS01DJGNlbnRlcnMKYGBgCgojIyMjIyAzLjYg55Wr5Ye65YiG576k57WQ5p6cCmBgYHtyIGZpZy53aWR0aD0yLjgzLCBmaWcuaGVpZ2h0PTMuMjN9CiMgRXh0cmFjdCBjbHVzdGVycwpYID0gS01DJGNsdXN0ZXIKCiMgUGxvdCB0aGUgaW1hZ2Ugd2l0aCB0aGUgY2x1c3RlcnMKZGltKFgpID0gYyhucm93KGhlYWx0aHlNYXRyaXgpLCBuY29sKGhlYWx0aHlNYXRyaXgpKQoKIyBQbG90IGltYWdlCnBhcihtYXI9YygxLDEsMSwxKSkKaW1hZ2UoWCwgYXhlcyA9IEZBTFNFLCBjb2w9cmFpbmJvdyhrKSkKYGBgCgojIyMjIyAzLjcg6K6A6YCy44CB6L2J5o+b5ris6Kmm5ZyW5b2iCmBgYHtyfQp0dW1vciA9IHJlYWQuY3N2KCJ0dW1vci5jc3YiLCBoZWFkZXI9RkFMU0UpCnR1bW9yTWF0cml4ID0gYXMubWF0cml4KHR1bW9yKQpkaW0odHVtb3JNYXRyaXgpCnR1bW9yVmVjdG9yID0gYXMudmVjdG9yKHR1bW9yTWF0cml4KQpsZW5ndGgodHVtb3JWZWN0b3IpCmBgYAoKIyMjIyMgMy44IOWwh+WOn+WcluW9ouS5i+WIhue+pOimj+WJh+Wll+eUqOWIsOa4rOippuWcluW9ogpgYGB7cn0KIyBBcHBseSBjbHVzdGVycyBmcm9tIGJlZm9yZSB0byBuZXcgaW1hZ2UsIHVzaW5nIHRoZSBmbGV4Y2x1c3QgcGFja2FnZQpsaWJyYXJ5KGZsZXhjbHVzdCkKdDAgPSBTeXMudGltZSgpCktNQy5rY2NhID0gZmxleGNsdXN0Ojphcy5rY2NhKEtNQywgaGVhbHRoeVZlY3RvcikgICAgICAgICMg5bu656uL5qih5Z6LCnR1bW9yQ2x1c3RlcnMgPSBwcmVkaWN0KEtNQy5rY2NhLCBuZXdkYXRhID0gdHVtb3JWZWN0b3IpICMg6YCy6KGM6aCQ5risKOi9ieaPmykKU3lzLnRpbWUoKSAtIHQwCmBgYAoKIyMjIyMgMy45IOWcluWDj+avlOi8gwpgYGB7ciBmaWcuaGVpZ2h0PTMuMiwgZmlnLndpZHRoPTZ9CiMgVmlzdWFsaXplIHRoZSBjbHVzdGVycwpkaW0odHVtb3JDbHVzdGVycykgPSBjKG5yb3codHVtb3JNYXRyaXgpLCBuY29sKHR1bW9yTWF0cml4KSkKCnBhcihtZnJvdz1jKDEsMiksIG1hcj1jKDEsMSwyLDEpKQppbWFnZShYLCBheGVzID0gRkFMU0UsIGNvbD1yYWluYm93KGspLCBtYWluPSJIZWFsdGh5IikKaW1hZ2UodCh0dW1vckNsdXN0ZXJzKVssNTcxOjFdLCBheGVzID0gRkFMU0UsIGNvbD1yYWluYm93KGspLCBtYWluPSJUdW1vciIpCmBgYAoKIyMjIyMg44CQ5a2457+S6YeN6bue44CRCgorIOmbhue+pOWIhuaekOWcqOWcluWDj+iZleeQhueahOaHieeUqAorIOWWruWNgOmalOiuiuaVuOeahOmbhue+pOWIhuaekAorIOmbhue+pOWIhuaekOaooeWeiwoKIyMjIyMg44CQ5ZWP6aGM6KiO6KuW44CRCgrlsaTntJrlvI/lkoxLLU1lYW5z6ZuG576k5YiG5p6Q5pyJ5LuA6bq85beu55Ww77yfIOWug+WAkeWIhuWIpeeUqOWcqOS7gOm6vOeLgOazge+8nwoKKyDlsaTntJrlvI/vvJoKICAgICArIOW+nuWWruethuizh+aWmemWi+Wni++8jOS4jeeUqOS4gOmWi+Wni+WwseaxuuWumuimgeWIhuaIkOWkmuWwkee+pO+8jOacgOe1guaJgOacieeahGNsdXN0ZXJz6YO95pyD5oiQ54K65LiA5YCLY2x1c3Rlcu+8iOmAmuW4uOmBqeeUqOaWvOS4jeimgeWkquWkp+eahOizh+aWmembhu+8iQogICAKKyBLLU1lYW5z77yaCiAgICArIOS6i+WFiOWumue+qeWlveWIh+aIkGvntYTvvIzmnIDntYLljbPmnIPlh7rnj77miYDpnIDliIbntYTntYTmlbjnmoTliIbnvqTntZDmnpzljbPkvr/vvIjos4fmlpnph4/lho3lpKfpg73og73ovJXmmJPlgZrlh7rkvoYg77yJCgoK6ZuG576k5YiG5p6Q5qih5Z6L5ZKM5pmu6YCa55qE6ZuG576k5YiG5p6Q5pyJ5LuA6bq85beu55Ww77yfIAoKKyDpm4bnvqTliIbmnpDmqKHlnovvvJoKICAgKyDpm4bnvqTliIbmnpDmqKHlnovoiIfmma7pgJrpm4bnvqTliIbmnpDmqKHlnovkuI3lkIzvvIzpm4bnvqTliIbmnpDmqKHlnovlj6/ku6XlsIfljp/lp4vos4fmlpnkuZ/liIbmiJBUcmFpbmluZyBEYXRh6IiHVGVzdGluZyBEYXRh5L6G5bu656uL5qih5Z6L77yM5YaN55Sx5bu656uL5Ye65L6G55qE6ZuG576k5YiG5p6Q5qih5Z6L5bCH5pu05aSa5pyq55+l55qE6LOH5paZ54K66YCy5Y6777yM6YCy6ICM6IO95aSg6Yed5bCN5LiN5ZCM576k5peP6YCy6KGM57WQ5p6c5LmL6aCQ5risCgorIOaZrumAmumbhue+pOWIhuaekO+8mgogICAgKyDnm67mqJnnlLHos4fmlpnliIbnvqTntZDmnpzogIzlrprvvIzogIzpnZ7kvoboh6rmlrx0cmFpbmluZyBkYXRh6aCQ5ris57WQ5p6c5LmL5qih5Z6L77yM6JeJ55Sx5YWp6aGe5Z6L77yI5bGk57Sa5byP44CBSy1tZWFuc++8ieeahOW7uuaooeaWueW8j++8jOWPr+S7peWwh+izh+aWmeS+neeFp+majuWxpOWwh+aJgOacieizh+aWmemAsuihjOWIhuWxpOe5quijveWHumRlbmRvcmdyYW3vvIzmiJbmmK/kvp3nhafmrLLliIbntYTmlbhr5b6X5Ye65YiG6aGe57WQ5p6cCgohW10oY2x1c3RlcmluZy5qcGcpCjxici8+Cgrku4DpurzmmYLlgJnpnIDopoHlu7rpm4bnvqTliIbmnpDmqKHlnovvvJ8g6ZuG576k5YiG5p6Q5qih5Z6L55qE55So5rOV77yfCgorIOeVtuizh+aWmeeVtuS4reaXj+e+pOe1hOWFp+W3rueVsOeUmuWkp++8jOS9huaYr+mAmeWFtuS4reWPiOacieS4gOe+pOS4gOe+pOeahOmXnOS/gu+8jOS9v+WIhue1hOW+jOe1hOWFp+W3rueVsOWwj+eahOaDheazgeeUoueUn+aZgu+8jOS+v+mBqeWQiOS9v+eUqOW7uue9rumbhue+pOWIhuaekOaooeWei++8jOiLpeaYr+iIh+WFtuS7luebo+edo+W8j+WtuOe/kuaWueazleS4gOWQjOWft+ihjO+8jOaJgOWBmuWHuumgkOa4rOS5i+a6lueiuuW6puacg+mBoOavlOaykuacieW7uumbhue+pOWIhuaekOaooeWei+mrmOWHuuioseWkmu+8jOmAsuiAjOS5n+iDveW5q+WKqeWBmuWHuuaxuuetluWIhuaekOOAggoKCuWcluWDj+iZleeQhuWSjOWcluWDj+i+qOitmOacieS7gOm6vOW3rueVsO+8nwoKKyDlnJblg4/omZXnkIbvvJoKICAgICsg5ZyW5YOP6JmV55CG5piv6LyD5Z+656SO55qE5ZyW5YOP6KO95L2c77yM6JeJ55Sx5bCH5ZyW5YOP5YCR57Sw5YiG5oiQ5LiN5ZCM55qEcGl4ZWzlvozntYTlkIjogIzmiJDvvIzlj6/ku6XorpPmiYDmnInnmoTlnJblg4/pg73og73ooqvliIblibLmiJDkuI3lkIznmoTlsI/llq7kvY3vvIzkuJTntYTmiJDnmoTmr4/kuIDllq7kvY3kvZTljp/lnJblg4/nmoTmr5TkvovkuZ/mlLjpl5zliLDlnJblg4/nmoTop6PmnpDluqbjgILvvIjmr4/kuIDlvLXlnJblg4/nmoTlhafpg6jomZXnkIbvvIkKICAgCisg5ZyW5YOP6L6o6K2Y77yaCiAgICArIOWcluWDj+i+qOitmOaYr+apn+WZqOWtuOe/kuaJgOaOoeeUqOeahOamguW/te+8jOiXieeUseWwh+ioseioseWkmuWkmumhnuS8vOeahOWclueJh+S4n+mAsui+qOitmOeahOW3peWFt+W+jO+8jOiDveWkoOi+qOWIpeWHuuW9oueLgOOAgemhj+iJsuetiee0sOevgOeahOW3rueVsO+8jOiAjOiLpeaYr+S4n+i2s+WkoOWkmueahOWcluWDj+mAsuWOu+apn+WZqOeahOipse+8jOWFtui+qOitmOa6lueiuuW6puacg+mdnuW4uOmrmOOAgu+8iOWkmuW8teWcluWDj+eahOeWiuWQiOWwjeavlOiZleeQhu+8iQoKCgo8YnI+CgotIC0gLQoKPGJyPjxicj48YnI+PGJyPjxicj4KCjxzdHlsZT4KLmNhcHRpb24gewogIGNvbG9yOiAjNzc3OwogIG1hcmdpbi10b3A6IDEwcHg7Cn0KcCBjb2RlIHsKICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsKfQpwcmUgewogIHdvcmQtYnJlYWs6IG5vcm1hbDsKICB3b3JkLXdyYXA6IG5vcm1hbDsKICBsaW5lLWhlaWdodDogMTsKfQpwcmUgY29kZSB7CiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7Cn0KcCxsaSB7CiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7Cn0KCi5yewogIGxpbmUtaGVpZ2h0OiAxLjI7Cn0KCnRpdGxlewogIGNvbG9yOiAjY2MwMDAwOwogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOwp9Cgpib2R5ewogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOwp9CgpoMSxoMixoMyxoNCxoNXsKICBjb2xvcjogIzAwODgwMDsKICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsKfQoKaDN7CiAgY29sb3I6ICNiMzZiMDA7CiAgYmFja2dyb3VuZDogI2ZmZTBiMzsKICBsaW5lLWhlaWdodDogMjsKICBmb250LXdlaWdodDogYm9sZDsKfQoKaDV7CiAgY29sb3I6ICMwMDYwMDA7CiAgYmFja2dyb3VuZDogI2ZmZmZlMDsKICBsaW5lLWhlaWdodDogMjsKICBmb250LXdlaWdodDogYm9sZDsKfQoKZW17CiAgY29sb3I6ICMwMDAwYzA7CiAgYmFja2dyb3VuZDogI2YwZjBmMDsKICB9Cjwvc3R5bGU+Cgo=