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)
library(FactoMineR)
library(factoextra)

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   Delicassen  
 Ch1:298   Reg1: 77   Min.   :0.477   Min.   :1.74   Min.   :0.477   Min.   :1.40   Min.   :0.477    Min.   :1.74  
 Ch2:142   Reg2: 47   1st Qu.:3.495   1st Qu.:3.19   1st Qu.:3.333   1st Qu.:2.87   1st Qu.:3.495    1st Qu.:3.19  
           Reg3:316   Median :3.930   Median :3.56   Median :3.677   Median :3.18   Median :3.930    Median :3.56  
                      Mean   :3.792   Mean   :3.53   Mean   :3.666   Mean   :3.17   Mean   :3.792    Mean   :3.53  
                      3rd Qu.:4.229   3rd Qu.:3.86   3rd Qu.:4.028   3rd Qu.:3.55   3rd Qu.:4.229    3rd Qu.:3.86  
                      Max.   :5.050   Max.   :4.87   Max.   :4.967   Max.   :4.78   Max.   :5.050    Max.   :4.87  
A2. 兩個區隔變數
hc = W[,3:4] %>% scale %>% dist %>% hclust  #scale做標準化、dist是距離、hclist需要的是距離矩陣
plot(hc)
rect.hclust(hc, k=5, border="red")  #k自己調,沒有標準切多少比較好

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  #改3:7欄位
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("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")  #一旦一兩萬點就做不出來了(罩門),就要用k-mean
dmx %>% as.matrix %>% dim               #meaning that we want to use euclidean distance
[1] 1664 1664
1.4 層級式集群分析
hclust1 = hclust(dmx, method = "ward.D")  #不同method會分出不同結果,但沒太多意義 
#The ward method cares about the distance between clusters using centroid distance, and also the variance in each of the clusters.
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)  #It divides our data points into the 10 clusters and then computes the average value of the action variable for each cluster.
      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)  #split看變數有幾個就切幾個資料框 #M是movie,從2開始因為1是片名
                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
                                                     #想看每群的區別變數的平均,並視覺化
                                                     #round是設定小數點#colMeans把每個資料框的平均值算出來
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)

【問題討論】

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

  • 目的是為了獲得新客戶、留存老客戶、提高客戶忠誠度
  • 企業若是不知道自己的會員分群,沒有區分過會員彼此之間的關係,以成本來說,有1000萬又要如何去分配,在管理上也就無所適從

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

  • 尺度縮減主要做視覺化,呈現方便
  • 尺度縮減可看出尺度與尺度之間的關係、也可從低尺度看出原本的尺度,讓人比較好理解

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

  • 使用集群分析,根據不同目標的特性將觀察值分成同質的組別,觀察其結果,可以用來區隔市場,並檢驗不同的市場策略。



2. Flower Image

2.1 整理資料
# Read data
flower = read.csv("data/flower.csv", header=FALSE)
# we have no headers in the CSV file because it only contains a matrix of intensity values.
# 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)  # this reflects the 50 times 50 intensity values that we had in our matrix.
[1] 2500
2.2 距離矩陣
# Compute distances
distance = dist(flowerVector, method = "euclidean")
2.3 層級式集群分析
# Hierarchical clustering
clusterIntensity = hclust(distance, method="ward.D")#the Ward’s method is a minimum variance method, which tries to find compact and spherical clusters.
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)  #dim:convert it into a matrix.
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)  #We need to make sure that R reads in the matrix appropriately.
dim(healthyMatrix)
[1] 566 646
3.2 畫出圖形
# Plot image
par(mar=c(1,1,1,1))  #For 8 bits per pixel images, we have 256 color levels ranging from zero to one.
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?

  • 資料量太大,無法使用層級式集群分析
  • It was impossible for us to use hierarchical clustering because of the high resolution of our image.
3.4 KMeans集群分析
# Run k-means
k = 5  #setting the number of clusters depends on exactly what you're trying to extract from the image.
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  #They are all less than 0.5.So they're all pretty close to 0.And this means that our images is pretty dark.
     [,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 將原圖形之分群規則套用到測試圖形
# Apply clusters from before to new image, using the flexclust package
library(flexclust)
t0 = Sys.time()
KMC.kcca = flexclust::as.kcca(KMC, healthyVector)        # 建立模型
tumorClusters = predict(KMC.kcca, newdata = tumorVector) # 進行預測(轉換)
Sys.time() - t0
Time difference of 26.31 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集群分析有什麼差異? 它們分別用在什麼狀況?

  • K-Means集群分析必須事先決定集群數目(K個群組),而層級式不用。
  • 如果觀察值的個數較多或資料檔非常龐大(通常觀察值在200個以上),採用K-Means集群 分析法較為適宜,。如果觀察值樣本不大,則採用階層式集群分析法較為適宜。

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

  • 普通集群分析著重在分幾群上面(例如分三群)
  • 集群分析模型著重在建模,選擇什麼樣的參數會影響什麼結果

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

  • 在有堆積如山資料的時候,希望利用自動或半自動的方式,發掘出隱藏在資料中的有用資訊。
  • 用法在於觀察參數調整後,模型的變化,分群的結果追求的不是模型的準度而是它的應用性,哪一個群集結果的產出最能讓人員解讀與進一步應用,才是適切的分群結果。因此如何快速的調整或找到適合的分群結果,才是重點。

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

  • 圖像處理是將圖像做一些強化或簡化的運算,凸顯出一些我們想要知道的特性,但是它的輸出資料還是一張圖像,譬如一張輪廓圖。
  • 圖像辨識就是必須有明確的文字或數字輸出,要明確到可以變成資料庫的資料,能提供搜尋、分析與比對之用的資訊。








LS0tDQp0aXRsZTogIkFTNi0wIOmbhue+pOWIhuaekCINCmF1dGhvcjogIkdST1VQMywgMjAxOC8wNy8yOSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxicj4NCg0KYGBge3J9DQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCJDIikNCnBhY2thZ2VzID0gYygNCiAgImRwbHlyIiwiZ2dwbG90MiIsImQzaGVhdG1hcCIsImdvb2dsZVZpcyIsImRldnRvb2xzIiwicGxvdGx5IiwgInhnYm9vc3QiLA0KICAibWFncml0dHIiLCJjYVRvb2xzIiwiUk9DUiIsImNvcnJwbG90IiwgInJwYXJ0IiwgInJwYXJ0LnBsb3QiLA0KICAiZG9QYXJhbGxlbCIsICJjYXJldCIsICJnbG1uZXQiLCAiTWF0cml4IiwgImUxMDcxIiwgInJhbmRvbUZvcmVzdCIsDQogICJmbGV4Y2x1c3QiLCAiRmFjdG9NaW5lUiIsICJmYWN0b2V4dHJhIg0KICApDQpleGlzdGluZyA9IGFzLmNoYXJhY3RlcihpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pDQpmb3IocGtnIGluIHBhY2thZ2VzWyEocGFja2FnZXMgJWluJSBleGlzdGluZyldKSBpbnN0YWxsLnBhY2thZ2VzKHBrZykNCmBgYA0KDQpgYGB7ciBlY2hvPVQsIG1lc3NhZ2U9RiwgY2FjaGU9Riwgd2FybmluZz1GfQ0Kcm0obGlzdD1scyhhbGw9VCkpDQpvcHRpb25zKGRpZ2l0cz00LCBzY2lwZW49MTIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmbGV4Y2x1c3QpDQpsaWJyYXJ5KEZhY3RvTWluZVIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpgYGANCg0KLSAtIC0NCg0KIyMjIEEuIOmbhue+pOWIhuaekOiIh+WwuuW6pue4rua4mw0KDQojIyMjIyBBMS4g5om555m85Lqk5piT6LOH5paZDQpgYGB7cn0NClcgPSByZWFkLmNzdignZGF0YS93aG9sZXNhbGVzLmNzdicpDQpXJENoYW5uZWwgPSBmYWN0b3IoIHBhc3RlMCgiQ2giLFckQ2hhbm5lbCkgKQ0KVyRSZWdpb24gPSBmYWN0b3IoIHBhc3RlMCgiUmVnIixXJFJlZ2lvbikgKQ0KV1szOjhdID0gbGFwcGx5KFdbMzo2XSwgbG9nLCBiYXNlPTEwKQ0Kc3VtbWFyeShXKQ0KYGBgDQoNCiMjIyMjIEEyLiDlhanlgIvljYDpmpTorormlbgNCmBgYHtyfQ0KaGMgPSBXWywzOjRdICU+JSBzY2FsZSAlPiUgZGlzdCAlPiUgaGNsdXN0ICAjc2NhbGXlgZrmqJnmupbljJbjgIFkaXN05piv6Led6Zui44CBaGNsaXN06ZyA6KaB55qE5piv6Led6Zui55+p6ZmjDQpwbG90KGhjKQ0KcmVjdC5oY2x1c3QoaGMsIGs9NSwgYm9yZGVyPSJyZWQiKSAgI2voh6rlt7Hoqr/vvIzmspLmnInmqJnmupbliIflpJrlsJHmr5TovIPlpb0NCmBgYA0KDQpgYGB7cn0NClckZ3JvdXAgPSBjdXRyZWUoaGMsIGs9NSkgJT4lIGZhY3Rvcg0KZ2dwbG90KFcsIGFlcyh4PUZyZXNoLCB5PU1pbGssIGNvbD1ncm91cCkpICsNCiAgZ2VvbV9wb2ludChzaXplPTMsIGFscGhhPTAuNSkgKyANCiAgdGhlbWVfbGlnaHQoKQ0KYGBgDQoNCiMjIyMjIEEzLiDlha3lgIvljYDpmpTorormlbgNCmBgYHtyfQ0KaGMgPSBXWywzOjddICU+JSBzY2FsZSAlPiUgZGlzdCAlPiUgaGNsdXN0ICAj5pS5Mzo35qyE5L2NDQpwbG90KGhjKQ0KVyRncm91cCA9IGZhY3RvcihjdXRyZWUoaGMsIGs9OCkpDQpyZWN0LmhjbHVzdChoYywgaz04LCBib3JkZXI9InJlZCIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KEZhY3RvTWluZVIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpmdml6X2RlbmQoDQogIGhjLCBrPTgsIHNob3dfbGFiZWxzPUYsIHJlY3Q9VCwgcmVjdF9maWxsPVQsDQogIGxhYmVsc190cmFja19oZWlnaHQ9MCwNCiAgcGFsZXR0ZT0idWNzY2diIiwgcmVjdF9ib3JkZXI9InVjc2NnYiIpICAj6IiH5LiK6Z2i54Sh55Ww77yM5L2G5q+U6LyD576O77yM6LeR5oy65LmFDQpgYGANCg0KIyMjIyMgQTQuIOWwuuW6pue4rua4myANCkRpbWVuc2lvbiBSZWR1Y3Rpb24gd2l0aCBQQ0EgKFByaW5jaXBsZSBDb21wb25lbnQgQW5hbHlzaXMsIOS4u+aIkOWIhuWIhuaekCkNCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9DQpXWywzOjhdICU+JSBQQ0EoZ3JhcGg9RikgJT4lIGZ2aXpfcGNhX2JpcGxvdCgNCiAgbGFiZWw9InZhciIsIGNvbC5pbmQ9VyRncm91cCwNCiAgcG9pbnRzaGFwZT0xOSwgbWVhbi5wb2ludD1GLCAgICAgICAgICAgICAgICAj6ZuG576k5YiG5p6Q5Li76KaB5YGa6LOH5paZ5o6i57Si77yM5bC65bqm57iu5rib5Li76KaB5YGa6KaW6Ka65YyWDQogIGFkZEVsbGlwc2VzPVQsIGVsbGlwc2UubGV2ZWw9MC43LA0KICBlbGxpcHNlLnR5cGUgPSAiY29udmV4IiwgcGFsZXR0ZT0idWNzY2diIiwgICPlj6/nnIvlh7rlsLrluqboiIflsLrluqbkuYvplpPnmoTpl5zkv4LjgIHkuZ/lj6/lvp7kvY7lsLrluqbnnIvlh7rljp/mnKznmoTlsLrluqYNCiAgcmVwZWw9VA0KICApDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIDEuIENsdXN0ZXIgQW5hbHlzaXMgZm9yIE1vdmllcyAgDQoNCioq5Li76KaB6K2w6aGM77ya5L6d6aGe5Z6LKEdlbnJlKeWwjembu+W9seWIhumhnioqDQoNCioq5a2457+S6YeN6bue77yaKioNCg0KKyDpm4bnvqTliIbmnpDnmoTln7rmnKzop4Dlv7UNCisg6Led6Zui55+p6Zmj77yaRGlzdGFuY2UgTWF0cml4DQorIOWxpOe0muW8j+mbhue+pOWIhuaekO+8mkhpZXJhcmNoaWNhbCBDbHVzdGVyIEFuYWx5c2lzDQorIOaoueeLgOWclihEZW5kcm9ncmFtKeeahOWIpOiugA0KKyDkvp3mk5rmqLnni4DlnJbmsbrlrpropoHliIblpJrlsJHnvqQNCisg5Lul576k57WE5bmz5Z2H5YC85qqi6KaW5ZCE5peP576k55qE5bGs5oCnDQoNCjxicj4NCg0KIyMjIyMgMS4xIOaVtOeQhuizh+aWmQ0KYGBge3J9DQpNID0gcmVhZC50YWJsZSgiZGF0YS9tb3ZpZUxlbnMudHh0IiwgaGVhZGVyPUZBTFNFLCBzZXA9InwiLHF1b3RlPSJcIiIpDQoNCiMgQXNzaWduIGNvbHVtbiBuYW1lcw0KY29sbmFtZXMoTSkgPSBjKA0KICAiSUQiLCAiVGl0bGUiLCAiUmVsZWFzZURhdGUiLCAiVmlkZW9SZWxlYXNlRGF0ZSIsICJJTURCIiwgDQogICJVbmtub3duIiwgIkFjdGlvbiIsICJBZHZlbnR1cmUiLCAiQW5pbWF0aW9uIiwgIkNoaWxkcmVucyIsIA0KICAiQ29tZWR5IiwgIkNyaW1lIiwgIkRvY3VtZW50YXJ5IiwgIkRyYW1hIiwgIkZhbnRhc3kiLCAiRmlsbU5vaXIiLCANCiAgIkhvcnJvciIsICJNdXNpY2FsIiwgIk15c3RlcnkiLCAiUm9tYW5jZSIsICJTY2lGaSIsICJUaHJpbGxlciIsDQogICJXYXIiLCAiV2VzdGVybiIpDQoNCiMgUmVtb3ZlIHVubmVjZXNzYXJ5IHZhcmlhYmxlcw0KTSRJRCA9IE5VTEwNCk0kUmVsZWFzZURhdGUgPSBOVUxMDQpNJFZpZGVvUmVsZWFzZURhdGUgPSBOVUxMDQpNJElNREIgPSBOVUxMDQoNCiMgUmVtb3ZlIGR1cGxpY2F0ZXMNCk0gPSB1bmlxdWUoTSkNCmBgYA0KDQojIyMjIyAxLjIg5qqi6KaW6LOH5paZDQpgYGB7cn0NCmhlYWQoTSwgNSkNCmBgYA0KDQpgYGB7cn0NCnN1bShNJENvbWVkeSkgICAgICAgICAgICAgIyDllpzliofniYcNCnN1bShNJFdlc3Rlcm4pICAgICAgICAgICAgIyDopb/pg6jniYcNCnN1bShNJFJvbWFuY2UgfCBNJERyYW1hKSAgIyDmtarmvKvliofmg4XniYcgDQpgYGANCg0KIyMjIyMgMS4zIOi3nembouefqemZow0KYGBge3J9DQpkbXg9IGRpc3QoTVsyOjIwXSwgbWV0aG9kPSJldWNsaWRlYW4iKSAgI+S4gOaXpuS4gOWFqeiQrOm7nuWwseWBmuS4jeWHuuS+huS6hijnvanploAp77yM5bCx6KaB55Soay1tZWFuDQpkbXggJT4lIGFzLm1hdHJpeCAlPiUgZGltICAgICAgICAgICAgICAgI21lYW5pbmcgdGhhdCB3ZSB3YW50IHRvIHVzZSBldWNsaWRlYW4gZGlzdGFuY2UNCmBgYA0KDQojIyMjIyAxLjQg5bGk57Sa5byP6ZuG576k5YiG5p6QDQpgYGB7cn0NCmhjbHVzdDEgPSBoY2x1c3QoZG14LCBtZXRob2QgPSAid2FyZC5EIikgICPkuI3lkIxtZXRob2TmnIPliIblh7rkuI3lkIzntZDmnpzvvIzkvYbmspLlpKrlpJrmhI/nvqkgDQojVGhlIHdhcmQgbWV0aG9kIGNhcmVzIGFib3V0IHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGNsdXN0ZXJzIHVzaW5nIGNlbnRyb2lkIGRpc3RhbmNlLCBhbmQgYWxzbyB0aGUgdmFyaWFuY2UgaW4gZWFjaCBvZiB0aGUgY2x1c3RlcnMuDQpgYGANCg0KIyMjIyMgMS41IOaqouimluaoueeLgOWclg0KYGBge3J9DQpwbG90KGhjbHVzdDEpDQpyZWN0LmhjbHVzdChoY2x1c3QxLCBrPTUsIGJvcmRlcj0icmVkIikNCmBgYA0KDQojIyMjIyAxLjYg5YiH5Ymy576k57WEDQpgYGB7cn0NCmdycCA9IGN1dHJlZShoY2x1c3QxLCBrID0gNSkNCnRhYmxlKGdycCkNCmBgYA0KDQojIyMjIyAxLjcg5qqi5p+l576k57WE5bGs5oCnDQpgYGB7cn0NCnRhcHBseShNJEFjdGlvbiwgZ3JwLCBtZWFuKSAgI+mAmuW4uOacg+aDs+efpemBk+avj+WAi+iuiuaVuOeahOW5s+Wdh+WAvCPntbHoqIjmmK/li5XkvZzniYfnmoTmr5TnjocNCnRhcHBseShNJFJvbWFuY2UsIGdycCwgbWVhbikgICNJdCBkaXZpZGVzIG91ciBkYXRhIHBvaW50cyBpbnRvIHRoZSAxMCBjbHVzdGVycyBhbmQgdGhlbiBjb21wdXRlcyB0aGUgYXZlcmFnZSB2YWx1ZSBvZiB0aGUgYWN0aW9uIHZhcmlhYmxlIGZvciBlYWNoIGNsdXN0ZXIuDQpgYGANCg0KIyMjIyMgMS44IFRoZSBgc2FwcGx5YC1gc3BsaXRgLWAuLi5gIENvbWJv77yaDQpgYGB7cn0NCnNhcHBseShzcGxpdChNWywyOjIwXSwgZ3JwKSwgY29sTWVhbnMpICU+JSByb3VuZCgzKSAgI3NwbGl055yL6K6K5pW45pyJ5bm+5YCL5bCx5YiH5bm+5YCL6LOH5paZ5qGGICNN5pivbW92aWXvvIzlvp4y6ZaL5aeL5Zug54K6MeaYr+eJh+WQjQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAj5oOz55yL5q+P576k55qE5Y2A5Yil6K6K5pW455qE5bmz5Z2H77yM5Lim6KaW6Ka65YyWDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNyb3VuZOaYr+ioreWumuWwj+aVuOm7niNjb2xNZWFuc+aKiuavj+WAi+izh+aWmeahhueahOW5s+Wdh+WAvOeul+WHuuS+hg0KYGBgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KDQojIyMjIyAxLjkg6LOH5paZ6KaW6Ka65YyWDQpgYGB7cn0NCmxheW91dChtYXRyaXgoYygxLDIsMiksIDMsIDEpKQ0KcGFyKG1hcj1jKDIsMywxLDEpLCBjZXg9MC44KQ0KdGFibGUoZ3JwKSAlPiUgYmFycGxvdChjb2w9Mzo3LCBuYW1lcy5hcmc9cGFzdGUwKCJHcm91cC0iLDE6NSkpDQpwYXIobWFyPWMoNiwzLDIsMSkpDQpzYXBwbHkoc3BsaXQoTVssMjoyMF0sIGdycCksIGNvbE1lYW5zKSAlPiUgdCAlPiUgDQogIGJhcnBsb3QoYmVzaWRlPVQsIGNvbD0zOjcsIGxhcz0yKQ0KDQpgYGANCg0KIyMjIyMg44CQ5ZWP6aGM6KiO6KuW44CRDQoNCuW+nueuoeeQhueahOinkuW6puS+hueci++8jOaIkeWAkeeCuueUmum6vOimgeWIhue+pO+8nyANCg0KKyDnm67nmoTmmK/ngrrkuobnjbLlvpfmlrDlrqLmiLbjgIHnlZnlrZjogIHlrqLmiLbjgIHmj5Dpq5jlrqLmiLblv6DoqqDluqYNCisg5LyB5qWt6Iul5piv5LiN55+l6YGT6Ieq5bex55qE5pyD5ZOh5YiG576k77yM5rKS5pyJ5Y2A5YiG6YGO5pyD5ZOh5b285q2k5LmL6ZaT55qE6Zec5L+C77yM5Lul5oiQ5pys5L6G6Kqq77yM5pyJMTAwMOiQrOWPiOimgeWmguS9leWOu+WIhumFje+8jOWcqOeuoeeQhuS4iuS5n+WwseeEoeaJgOmBqeW+ng0KDQoNCuaIkeWAkeeCuueUmum6vOimgeWBmuWwuuW6pue4rua4m++8nyANCg0KKyDlsLrluqbnuK7muJvkuLvopoHlgZroppboprrljJbvvIzlkYjnj77mlrnkvr8NCisg5bC65bqm57iu5rib5Y+v55yL5Ye65bC65bqm6IiH5bC65bqm5LmL6ZaT55qE6Zec5L+C44CB5Lmf5Y+v5b6e5L2O5bC65bqm55yL5Ye65Y6f5pys55qE5bC65bqm77yM6K6T5Lq65q+U6LyD5aW955CG6KejDQoNCg0K5oiR5YCR6KaB5aaC5L2V5oqK6ZuG576k5YiG5p6Q55qE57WQ5p6c6L2J5YyW54K6562W55Wl5ZGi77yfIA0KDQorIOS9v+eUqOmbhue+pOWIhuaekO+8jOagueaTmuS4jeWQjOebruaomeeahOeJueaAp+Wwh+ingOWvn+WAvOWIhuaIkOWQjOizqueahOe1hOWIpe+8jOingOWvn+WFtue1kOaenO+8jOWPr+S7peeUqOS+huWNgOmalOW4guWgtO+8jOS4puaqoumpl+S4jeWQjOeahOW4guWgtOetlueVpeOAgg0KDQoNCjxicj4NCg0KLSAtIC0NCg0KIyMjIDIuIEZsb3dlciBJbWFnZQ0KDQojIyMjIyAyLjEg5pW055CG6LOH5paZDQpgYGB7cn0NCiMgUmVhZCBkYXRhDQpmbG93ZXIgPSByZWFkLmNzdigiZGF0YS9mbG93ZXIuY3N2IiwgaGVhZGVyPUZBTFNFKQ0KIyB3ZSBoYXZlIG5vIGhlYWRlcnMgaW4gdGhlIENTViBmaWxlIGJlY2F1c2UgaXQgb25seSBjb250YWlucyBhIG1hdHJpeCBvZiBpbnRlbnNpdHkgdmFsdWVzLg0KIyBDaGFuZ2UgdGhlIGRhdGEgdHlwZSB0byBtYXRyaXgNCmZsb3dlck1hdHJpeCA9IGFzLm1hdHJpeChmbG93ZXIpDQpkaW0oZmxvd2VyTWF0cml4KQ0KDQojIFR1cm4gbWF0cml4IGludG8gYSB2ZWN0b3INCmZsb3dlclZlY3RvciA9IGFzLnZlY3RvcihmbG93ZXJNYXRyaXgpDQpsZW5ndGgoZmxvd2VyVmVjdG9yKSAgIyB0aGlzIHJlZmxlY3RzIHRoZSA1MCB0aW1lcyA1MCBpbnRlbnNpdHkgdmFsdWVzIHRoYXQgd2UgaGFkIGluIG91ciBtYXRyaXguDQpgYGANCg0KIyMjIyMgMi4yIOi3nembouefqemZow0KYGBge3J9DQojIENvbXB1dGUgZGlzdGFuY2VzDQpkaXN0YW5jZSA9IGRpc3QoZmxvd2VyVmVjdG9yLCBtZXRob2QgPSAiZXVjbGlkZWFuIikNCmBgYA0KDQojIyMjIyAyLjMg5bGk57Sa5byP6ZuG576k5YiG5p6QDQpgYGB7cn0NCiMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcNCmNsdXN0ZXJJbnRlbnNpdHkgPSBoY2x1c3QoZGlzdGFuY2UsIG1ldGhvZD0id2FyZC5EIikjdGhlIFdhcmTigJlzIG1ldGhvZCBpcyBhIG1pbmltdW0gdmFyaWFuY2UgbWV0aG9kLCB3aGljaCB0cmllcyB0byBmaW5kIGNvbXBhY3QgYW5kIHNwaGVyaWNhbCBjbHVzdGVycy4NCmBgYA0KDQojIyMjIyAyLjQg5qi554uA5ZyWDQpgYGB7cn0NCiMgUGxvdCB0aGUgZGVuZHJvZ3JhbQ0KcGxvdChjbHVzdGVySW50ZW5zaXR5KQ0KIyBTZWxlY3QgMyBjbHVzdGVycw0KcmVjdC5oY2x1c3QoY2x1c3RlckludGVuc2l0eSwgayA9IDMsIGJvcmRlciA9ICJyZWQiKQ0KYGBgDQoNCiMjIyMjIOWIh+WJsue+pOe1hA0KYGBge3J9DQpmbG93ZXJDbHVzdGVycyA9IGN1dHJlZShjbHVzdGVySW50ZW5zaXR5LCBrID0gMykNCnRhYmxlKGZsb3dlckNsdXN0ZXJzKQ0KIyBmbG93ZXJDbHVzdGVycw0KYGBgDQoNCiMjIyMjIOaXj+e+pOW5s+WdhyjnlavntKDpoY/oibLmt7Hmt7rluqYpDQpgYGB7cn0NCiMgRmluZCBtZWFuIGludGVuc2l0eSB2YWx1ZXMNCnRhcHBseShmbG93ZXJWZWN0b3IsIGZsb3dlckNsdXN0ZXJzLCBtZWFuKQ0KYGBgDQoNCiMjIyMjIOWcluWDj+avlOi8gw0KYGBge3IgZmlnLmhlaWdodD0zLjIsIGZpZy53aWR0aD02LjR9DQojIFBsb3QgdGhlIGltYWdlIGFuZCB0aGUgY2x1c3RlcnMNCmRpbShmbG93ZXJDbHVzdGVycykgPSBjKDUwLDUwKSAgI2RpbTpjb252ZXJ0IGl0IGludG8gYSBtYXRyaXguDQpwYXIobWZyb3c9YygxLDIpLCBtYXI9YygyLDIsMiwyKSkNCg0KIyBPcmlnaW5hbCBpbWFnZQ0KaW1hZ2UoZmxvd2VyTWF0cml4LGF4ZXM9RkFMU0UsY29sPWdyZXkoc2VxKDAsMSxsZW5ndGg9MjU2KSksbWFpbj0iT3JpZ2luYWwiKQ0KDQojIE5ldyBpbWFnZQ0KaW1hZ2UoZmxvd2VyQ2x1c3RlcnMsIGF4ZXMgPSBGQUxTRSwgbWFpbj0iMyBDbHVzdGVyIikNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMgMy4gTVJJIEltYWdlDQoNCiMjIyMjIDMuMSDmlbTnkIbos4fmlpkNCmBgYHtyfQ0KIyBSZWFkIGRhdGENCmhlYWx0aHkgPSByZWFkLmNzdigiZGF0YS9oZWFsdGh5LmNzdiIsIGhlYWRlcj1GQUxTRSkNCmhlYWx0aHlNYXRyaXggPSBhcy5tYXRyaXgoaGVhbHRoeSkgICNXZSBuZWVkIHRvIG1ha2Ugc3VyZSB0aGF0IFIgcmVhZHMgaW4gdGhlIG1hdHJpeCBhcHByb3ByaWF0ZWx5Lg0KZGltKGhlYWx0aHlNYXRyaXgpDQpgYGANCg0KIyMjIyMgMy4yIOeVq+WHuuWcluW9og0KYGBge3IgZmlnLndpZHRoPTIuODMsIGZpZy5oZWlnaHQ9My4yM30NCiMgUGxvdCBpbWFnZQ0KcGFyKG1hcj1jKDEsMSwxLDEpKSAgI0ZvciA4IGJpdHMgcGVyIHBpeGVsIGltYWdlcywgd2UgaGF2ZSAyNTYgY29sb3IgbGV2ZWxzIHJhbmdpbmcgZnJvbSB6ZXJvIHRvIG9uZS4NCmltYWdlKGhlYWx0aHlNYXRyaXgsYXhlcz1GQUxTRSxjb2w9Z3JleShzZXEoMCwxLGxlbmd0aD0yNTYpKSkNCmBgYA0KDQojIyMjIyAzLjMg6Led6Zui55+p6ZmjDQpgYGB7cn0NCiMgQ29tcHV0ZSBkaXN0YW5jZXMNCmhlYWx0aHlWZWN0b3IgPSBhcy52ZWN0b3IoaGVhbHRoeU1hdHJpeCkNCmRpc3RhbmNlID0gZGlzdChoZWFsdGh5VmVjdG9yLCBtZXRob2QgPSAiZXVjbGlkZWFuIikNCmBgYA0KDQoqKuOAkFHjgJEqKiBXaGF0IGlzIHRoZSBwcm9ibGVtPw0KDQorIOizh+aWmemHj+WkquWkp++8jOeEoeazleS9v+eUqOWxpOe0muW8j+mbhue+pOWIhuaekA0KKyBJdCB3YXMgaW1wb3NzaWJsZSBmb3IgdXMgdG8gdXNlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGJlY2F1c2Ugb2YgdGhlIGhpZ2ggcmVzb2x1dGlvbiBvZiBvdXIgaW1hZ2UuDQoNCg0KIyMjIyMgMy40IEtNZWFuc+mbhue+pOWIhuaekA0KYGBge3J9DQojIFJ1biBrLW1lYW5zDQprID0gNSAgI3NldHRpbmcgdGhlIG51bWJlciBvZiBjbHVzdGVycyBkZXBlbmRzIG9uIGV4YWN0bHkgd2hhdCB5b3UncmUgdHJ5aW5nIHRvIGV4dHJhY3QgZnJvbSB0aGUgaW1hZ2UuDQpzZXQuc2VlZCgxKQ0KS01DID0ga21lYW5zKGhlYWx0aHlWZWN0b3IsIGNlbnRlcnMgPSBrLCBpdGVyLm1heCA9IDEwMDApDQpgYGANCg0KIyMjIyMgMy41IOaqouafpeWIhue+pOe1kOaenA0KYGBge3J9DQojIFZpZXcoS01DKQ0KdGFibGUoS01DJGNsdXN0ZXIpDQpLTUMkY2VudGVycyAgI1RoZXkgYXJlIGFsbCBsZXNzIHRoYW4gMC41LlNvIHRoZXkncmUgYWxsIHByZXR0eSBjbG9zZSB0byAwLkFuZCB0aGlzIG1lYW5zIHRoYXQgb3VyIGltYWdlcyBpcyBwcmV0dHkgZGFyay4NCmBgYA0KDQojIyMjIyAzLjYg55Wr5Ye65YiG576k57WQ5p6cDQpgYGB7ciBmaWcud2lkdGg9Mi44MywgZmlnLmhlaWdodD0zLjIzfQ0KIyBFeHRyYWN0IGNsdXN0ZXJzDQpYID0gS01DJGNsdXN0ZXINCg0KIyBQbG90IHRoZSBpbWFnZSB3aXRoIHRoZSBjbHVzdGVycw0KZGltKFgpID0gYyhucm93KGhlYWx0aHlNYXRyaXgpLCBuY29sKGhlYWx0aHlNYXRyaXgpKQ0KDQojIFBsb3QgaW1hZ2UNCnBhcihtYXI9YygxLDEsMSwxKSkNCmltYWdlKFgsIGF4ZXMgPSBGQUxTRSwgY29sPXJhaW5ib3coaykpDQpgYGANCg0KIyMjIyMgMy43IOiugOmAsuOAgei9ieaPm+a4rOippuWcluW9og0KYGBge3J9DQp0dW1vciA9IHJlYWQuY3N2KCJkYXRhL3R1bW9yLmNzdiIsIGhlYWRlcj1GQUxTRSkNCnR1bW9yTWF0cml4ID0gYXMubWF0cml4KHR1bW9yKQ0KZGltKHR1bW9yTWF0cml4KQ0KdHVtb3JWZWN0b3IgPSBhcy52ZWN0b3IodHVtb3JNYXRyaXgpDQpsZW5ndGgodHVtb3JWZWN0b3IpDQpgYGANCg0KIyMjIyMgMy44IOWwh+WOn+WcluW9ouS5i+WIhue+pOimj+WJh+Wll+eUqOWIsOa4rOippuWcluW9og0KYGBge3J9DQojIEFwcGx5IGNsdXN0ZXJzIGZyb20gYmVmb3JlIHRvIG5ldyBpbWFnZSwgdXNpbmcgdGhlIGZsZXhjbHVzdCBwYWNrYWdlDQpsaWJyYXJ5KGZsZXhjbHVzdCkNCnQwID0gU3lzLnRpbWUoKQ0KS01DLmtjY2EgPSBmbGV4Y2x1c3Q6OmFzLmtjY2EoS01DLCBoZWFsdGh5VmVjdG9yKSAgICAgICAgIyDlu7rnq4vmqKHlnosNCnR1bW9yQ2x1c3RlcnMgPSBwcmVkaWN0KEtNQy5rY2NhLCBuZXdkYXRhID0gdHVtb3JWZWN0b3IpICMg6YCy6KGM6aCQ5risKOi9ieaPmykNClN5cy50aW1lKCkgLSB0MA0KYGBgDQoNCiMjIyMjIDMuOSDlnJblg4/mr5TovIMNCmBgYHtyIGZpZy5oZWlnaHQ9My4yLCBmaWcud2lkdGg9Nn0NCiMgVmlzdWFsaXplIHRoZSBjbHVzdGVycw0KZGltKHR1bW9yQ2x1c3RlcnMpID0gYyhucm93KHR1bW9yTWF0cml4KSwgbmNvbCh0dW1vck1hdHJpeCkpDQoNCnBhcihtZnJvdz1jKDEsMiksIG1hcj1jKDEsMSwyLDEpKQ0KaW1hZ2UoWCwgYXhlcyA9IEZBTFNFLCBjb2w9cmFpbmJvdyhrKSwgbWFpbj0iSGVhbHRoeSIpDQppbWFnZSh0KHR1bW9yQ2x1c3RlcnMpWyw1NzE6MV0sIGF4ZXMgPSBGQUxTRSwgY29sPXJhaW5ib3coayksIG1haW49IlR1bW9yIikNCmBgYA0KDQojIyMjIyDjgJDlrbjnv5Lph43pu57jgJENCg0KKyDpm4bnvqTliIbmnpDlnKjlnJblg4/omZXnkIbnmoTmh4nnlKgNCisg5Zau5Y2A6ZqU6K6K5pW455qE6ZuG576k5YiG5p6QDQorIOmbhue+pOWIhuaekOaooeWeiw0KDQojIyMjIyDjgJDllY/poYzoqI7oq5bjgJENCg0K5bGk57Sa5byP5ZKMSy1NZWFuc+mbhue+pOWIhuaekOacieS7gOm6vOW3rueVsO+8nyDlroPlgJHliIbliKXnlKjlnKjku4Dpurzni4Dms4HvvJ8NCg0KKyBLLU1lYW5z6ZuG576k5YiG5p6Q5b+F6aCI5LqL5YWI5rG65a6a6ZuG576k5pW455uu77yIS+WAi+e+pOe1hO+8ie+8jOiAjOWxpOe0muW8j+S4jeeUqOOAgg0KKyDlpoLmnpzop4Dlr5/lgLznmoTlgIvmlbjovIPlpJrmiJbos4fmlpnmqpTpnZ7luLjpvpDlpKfvvIjpgJrluLjop4Dlr5/lgLzlnKgyMDDlgIvku6XkuIrvvInvvIzmjqHnlKhLLU1lYW5z6ZuG576kDQrliIbmnpDms5XovIPngrrpganlrpzvvIzjgILlpoLmnpzop4Dlr5/lgLzmqKPmnKzkuI3lpKfvvIzliYfmjqHnlKjpmo7lsaTlvI/pm4bnvqTliIbmnpDms5XovIPngrrpganlrpzjgIINCg0K6ZuG576k5YiG5p6Q5qih5Z6L5ZKM5pmu6YCa55qE6ZuG576k5YiG5p6Q5pyJ5LuA6bq85beu55Ww77yfIA0KDQorIOaZrumAmumbhue+pOWIhuaekOiRl+mHjeWcqOWIhuW5vue+pOS4iumdoijkvovlpoLliIbkuInnvqQpDQorIOmbhue+pOWIhuaekOaooeWei+iRl+mHjeWcqOW7uuaooe+8jOmBuOaTh+S7gOm6vOaoo+eahOWPg+aVuOacg+W9semfv+S7gOm6vOe1kOaenA0KDQrku4DpurzmmYLlgJnpnIDopoHlu7rpm4bnvqTliIbmnpDmqKHlnovvvJ8g6ZuG576k5YiG5p6Q5qih5Z6L55qE55So5rOV77yfDQoNCisg5Zyo5pyJ5aCG56mN5aaC5bGx6LOH5paZ55qE5pmC5YCZ77yM5biM5pyb5Yip55So6Ieq5YuV5oiW5Y2K6Ieq5YuV55qE5pa55byP77yM55m85o6Y5Ye66Zqx6JeP5Zyo6LOH5paZ5Lit55qE5pyJ55So6LOH6KiK44CCDQorIOeUqOazleWcqOaWvOingOWvn+WPg+aVuOiqv+aVtOW+jO+8jOaooeWei+eahOiuiuWMlu+8jOWIhue+pOeahOe1kOaenOi/veaxgueahOS4jeaYr+aooeWei+eahOa6luW6puiAjOaYr+Wug+eahOaHieeUqOaAp++8jOWTquS4gOWAi+e+pOmbhue1kOaenOeahOeUouWHuuacgOiDveiuk+S6uuWToeino+iugOiIh+mAsuS4gOatpeaHieeUqO+8jOaJjeaYr+mBqeWIh+eahOWIhue+pOe1kOaenOOAguWboOatpOWmguS9leW/q+mAn+eahOiqv+aVtOaIluaJvuWIsOmBqeWQiOeahOWIhue+pOe1kOaenO+8jOaJjeaYr+mHjem7nuOAgg0KDQrlnJblg4/omZXnkIblkozlnJblg4/ovqjorZjmnInku4Dpurzlt67nlbDvvJ8NCg0KKyDlnJblg4/omZXnkIbmmK/lsIflnJblg4/lgZrkuIDkupvlvLfljJbmiJbnsKHljJbnmoTpgYvnrpfvvIzlh7jpoa/lh7rkuIDkupvmiJHlgJHmg7PopoHnn6XpgZPnmoTnibnmgKfvvIzkvYbmmK/lroPnmoTovLjlh7ros4fmlpnpgoTmmK/kuIDlvLXlnJblg4/vvIzorazlpoLkuIDlvLXovKrlu5PlnJbjgIINCisg5ZyW5YOP6L6o6K2Y5bCx5piv5b+F6aCI5pyJ5piO56K655qE5paH5a2X5oiW5pW45a2X6Ly45Ye677yM6KaB5piO56K65Yiw5Y+v5Lul6K6K5oiQ6LOH5paZ5bqr55qE6LOH5paZ77yM6IO95o+Q5L6b5pCc5bCL44CB5YiG5p6Q6IiH5q+U5bCN5LmL55So55qE6LOH6KiK44CCDQoNCjxicj4NCg0KLSAtIC0NCg0KPGJyPjxicj48YnI+PGJyPjxicj4NCg0KPHN0eWxlPg0KLmNhcHRpb24gew0KICBjb2xvcjogIzc3NzsNCiAgbWFyZ2luLXRvcDogMTBweDsNCn0NCnAgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcHJlIHsNCiAgd29yZC1icmVhazogbm9ybWFsOw0KICB3b3JkLXdyYXA6IG5vcm1hbDsNCiAgbGluZS1oZWlnaHQ6IDE7DQp9DQpwcmUgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcCxsaSB7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQoucnsNCiAgbGluZS1oZWlnaHQ6IDEuMjsNCn0NCg0KdGl0bGV7DQogIGNvbG9yOiAjY2MwMDAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KYm9keXsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgxLGgyLGgzLGg0LGg1ew0KICBjb2xvcjogIzAwODgwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgzew0KICBjb2xvcjogI2IzNmIwMDsNCiAgYmFja2dyb3VuZDogI2ZmZTBiMzsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoNXsNCiAgY29sb3I6ICMwMDYwMDA7DQogIGJhY2tncm91bmQ6ICNmZmZmZTA7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KZW17DQogIGNvbG9yOiAjMDAwMGMwOw0KICBiYWNrZ3JvdW5kOiAjZjBmMGYwOw0KICB9DQo8L3N0eWxlPg0KDQo=