主要議題:依字頻表對文章分群

學習重點:

rm(list=ls(all=T))
Sys.setlocale("LC_ALL","C")
[1] "C"
options(digits=4, scipen=12)
library(dplyr)



0. 名詞解釋(上課筆記)

1.1 資料探勘

剛開始得到一份資料,還不清楚整個資料的樣貌,沒有目的性的,多多嘗試就會發現一些有趣的現象。EX.AS6-0 Wholesales, Movies

1.2 分群管理(市場區隔)

為了要分群管理(有目的性),區隔變數的選擇會選跟那些目的有關的來做市場區隔。EX.AS6-2 Airlines

1.3 形態偵測(預測性診斷)

這種分群會分得很細,但重點不是在分群,而是為了去找那些少數幾個長相特別、明顯有特徵的小群(EX.60個裡面5~6個準確度高於平均),並拿這些顯著不同的幾種pattern去做預測。

1.4 分群預測(集群分析模型、分群預測模型)
  • 分完群再做預測(EX.很多小疾病會導致心臟病,但每個歷程皆不同)以專業知識判斷來分群,再來做預測。EX.AS6-3 Stocks
  • 數類模型(可以直接從數量上看出關係的模型)的分群可以交給機器去處理,因為純粹以數字、統計顯著性來分群,機器做的一定比人好。
  • 但若需要靠專業知識來判斷(EX.股票、醫療),就要先以人類專家的判斷做分群,再給機器做接下來的預測,相輔相乘,通常預測也會比較準確。

1. Hierarchical Clustering

1.1 字頻表、距離矩陣、階層式集群分析

Let’s start by building a hierarchical clustering model. First, read the data set into R. Then, compute the distances (using method=“euclidean”), and use hclust to build the model (using method=“ward.D”). You should cluster on all of the variables.

D = read.csv("dailykos.csv")
dim(D)
[1] 3430 1545
# 字頻表: Document Term Matrix
D[1:20, 1:10]
   abandon abc ability abortion absolute abstain abu abuse accept
1        0   0       0        0        0       0   0     0      0
2        0   0       0        0        0       0   0     0      0
3        0   0       0        0        0       1   0     0      0
4        0   0       0        0        0       0   0     0      0
5        0   0       0        0        0       0   0     0      0
6        0   0       0        0        0       0   0     0      0
7        0   0       0        0        0       0   0     0      0
8        0   0       0        0        0       0   0     0      0
9        0   0       0        0        0       0   0     0      0
10       0   0       0        0        0       0   0     0      0
11       0   0       0        0        0       0   0     0      0
12       0   0       0        0        0       1   0     0      0
13       0   0       0        0        0       0   0     0      0
14       0   0       0        0        0       0   0     0      0
15       0   0       0        0        0       0   0     0      0
16       0   0       0        0        0       0   0     0      0
17       0   0       0        0        0       0   0     0      0
18       0   0       0        0        0       0   0     0      0
19       0   0       0        0        0       0   0     0      0
20       0   0       0        0        0       0   0     0      0
   access
1       0
2       0
3       0
4       0
5       0
6       0
7       0
8       0
9       0
10      0
11      0
12      0
13      0
14      0
15      0
16      0
17      0
18      0
19      0
20      0
# 距離矩陣: Distance Matrix
t0 = Sys.time()
dist = dist(D, method = "euclidean")
Sys.time() - t0
Time difference of 3.501 mins

Running the dist function will probably take you a while. Why? Select all that apply.

# 階層式集群分析: Hierarchical Clustering Analysis
t0 = Sys.time()
clustD = hclust(dist, method = "ward.D")
Sys.time() - t0
Time difference of 0.625 secs

Plot the dendrogram of your hierarchical clustering model.

table(clustGroup,clustKM$cluster)
          
clustGroup    1    2    3    4    5    6    7
         1    3   11   64 1045   32    0  111
         2    0    0    0    0    0  320    1
         3   85   10   42   79  126    8   24
         4   10    5    0    0    1    0  123
         5   48    0  171  145    3    1   39
         6    0    2    0  712    0    0    0
         7    0  116    0   82    1    0   10
1.2 從樹狀圖判斷群數

Just looking at the dendrogram,

which of the following seem like good choices for the number of clusters? Select all that apply.

  • 2
  • 3
  • The choices 2 and 3 are good cluster choices according to the dendrogram, because there is a lot of space between the horizontal lines in the dendrogram in those cut off spots.
1.3 從應用決定群數

In this problem, we are trying to cluster news articles or blog posts into groups. This can be used to show readers categories to choose from when trying to decide what to read. Just thinking about this application,

what are good choices for the number of clusters? Select all that apply.

  • 7
  • 8
  • It is probably better to show the reader more categories than 2 or 3. These categories would probably be too broad to be useful. Seven or eight categories seems more reasonable.
1.4 依群組分割資料

Let’s pick 7 clusters. This number is reasonable according to the dendrogram, and also seems reasonable for the application. Use the cutree function to split your data into 7 clusters.

clustGroup = cutree(clustD, k=7)
clust1 = subset(D, clustGroup==1) # the most obs.
clust2 = subset(D, clustGroup==2)
clust3 = subset(D, clustGroup==3) # 374 obs.
clust4 = subset(D, clustGroup==4) # the fewest obs.
clust5 = subset(D, clustGroup==5)
clust6 = subset(D, clustGroup==6)
clust7 = subset(D, clustGroup==7)
clustHi = split(D, clustGroup) # 也可以將以上code簡化,用此行來直接分群

Now, we don’t really want to run tapply on every single variable when we have over 1,000 different variables. Let’s instead use the subset function to subset our data by cluster. Create 7 new datasets, each containing the observations from one of the clusters.

How many observations are in cluster 3?

nrow(clust3) # 374 obs.
[1] 374
View(clustHi) # 或是從環境變數直接看每個群集有幾個變數

Which cluster has the most observations?

  • 1

Which cluster has the fewest observations?

  • 4
1.5 找出第一族群中最常見的字辭

Instead of looking at the average value in each variable individually, we’ll just look at the top 6 words in each cluster. To do this for cluster 1, type the following in your R console (where “HierCluster1” should be replaced with the name of your first cluster subset):

tail(sort(colMeans(HierCluster1)))

This computes the mean frequency values of each of the words in cluster 1, and then outputs the 6 words that occur the most frequently. The colMeans function computes the column (word) means, the sort function orders the words in increasing order of the mean values, and the tail function outputs the last 6 words listed, which are the ones with the largest column means.

What is the most frequent word in this cluster, in terms of average value? Enter the word exactly how you see it in the output:

tail(sort(colMeans(clust1))) # bush
     state republican       poll   democrat      kerry       bush 
    0.7575     0.7591     0.9036     0.9194     1.0624     1.7054 
# colMeans是以欄(這裡的每個欄位變數都是一個word)為單位來計算平均值,就此例題,欄位的平均值代表該word出現的比率。

This computes the mean frequency values of each of the words in cluster 1, and then outputs the 6 words that occur the most frequently.

1.6 找出各族群中最常見的字辭

Now repeat the command given in the previous problem for each of the other clusters, and answer the following questions.

tail(sort(colMeans(clust2))) 
     bush  democrat challenge      vote      poll  november 
    2.847     2.850     4.097     4.399     4.847    10.340 
tail(sort(colMeans(clust3))) 
     elect    parties      state republican   democrat       bush 
     1.647      1.666      2.321      2.524      3.824      4.406 
tail(sort(colMeans(clust4))) 
campaign    voter presided     poll     bush    kerry 
   1.432    1.540    1.626    3.590    7.835    8.439 
tail(sort(colMeans(clust5))) 
      american       presided administration            war 
         1.091          1.120          1.231          1.776 
          iraq           bush 
         2.428          3.941 
tail(sort(colMeans(clust6)))
    race     bush    kerry    elect democrat     poll 
  0.4580   0.4888   0.5168   0.5350   0.5644   0.5812 
tail(sort(colMeans(clust7)))
democrat    clark   edward     poll    kerry     dean 
   2.148    2.498    2.608    2.766    3.952    5.804 

Which words best describe cluster 2?

  • november, poll, vote
  • 由此可看出群集2的人,討論許多關於11月時的選舉。

Which cluster could best be described as the cluster related to the Iraq war?

  • 5
  • Iraq的關鍵字只出現在群集5,並且高達2.428,其他的關鍵字有war,美國總統Bush和American,也反應出跟伊拉克戰爭特別相關。

In 2004, one of the candidates for the Democratic nomination for the President of the United States was Howard Dean, John Kerry was the candidate who won the democratic nomination, and John Edwards with the running mate of John Kerry (the Vice President nominee). Given this information,

which cluster best corresponds to the democratic party?

  • 7
  • 群集7除了有democrat關鍵字以外,Edward,Kerry和Dean都是民主黨的選舉人代表或支持者,因此即使democrat本身出現的次數比其他群集中還少,由於整個群集都是由民主黨相關的成分所組成,因此最有可能是民主黨。



2 K-Means Clustering

2.1 K-Means集群分析

Now, run k-means clustering, setting the seed to 1000 right before you run the kmeans function. Again, pick the number of clusters equal to 7. You don’t need to add the iters.max argument.

library(caTools)
set.seed(1000)
clustKM = kmeans(D, centers=7)
clustKMgroup = split(D, clustKM$cluster)
nrow(clustKM[[3]])
NULL
View(clustKM)
# cluster4 has the most obs, while cluster2 has the fewest obs.

Subset your data into the 7 clusters (7 new datasets) by using the “cluster” variable of your kmeans output.

How many observations are in Cluster 3?

  • 277
  • 由於K means的分群方式跟Hierarchical不同,得出各個群集中的obs值也自然不同,並且所有群集的順序(cluster 1~7)是沒有意義的,只是一個代號。

Which cluster has the most observations?

  • 4

Which cluster has the fewest number of observations?

  • 2
2.2 找出各族群中最常見的字辭

Now, output the six most frequent words in each cluster, like we did in the previous problem, for each of the k-means clusters.

tail(sort(colMeans(clustKMgroup[[1]])))
         state           iraq          kerry administration 
         1.610          1.616          1.637          2.664 
      presided           bush 
         2.767         11.432 
tail(sort(colMeans(clustKMgroup[[2]])))
primaries  democrat    edward     clark     kerry      dean 
    2.319     2.694     2.799     3.090     4.979     8.278 
tail(sort(colMeans(clustKMgroup[[3]])))
administration          iraqi       american           bush 
         1.390          1.610          1.686          2.610 
           war           iraq 
         3.025          4.094 
tail(sort(colMeans(clustKMgroup[[4]])))
     elect republican      kerry       poll   democrat       bush 
    0.6011     0.6175     0.6495     0.7475     0.7891     1.1474 
tail(sort(colMeans(clustKMgroup[[5]])))
      race     senate      state    parties republican   democrat 
     2.485      2.650      3.521      3.620      4.638      6.994 
tail(sort(colMeans(clustKMgroup[[6]])))
 democrat      bush challenge      vote      poll  november 
    2.900     2.960     4.122     4.447     4.872    10.371 
tail(sort(colMeans(clustKMgroup[[7]])))
presided    voter campaign     poll     bush    kerry 
   1.325    1.334    1.383    2.789    5.971    6.481 

Which k-means cluster best corresponds to the Iraq War?

  • 3
  • 雖然群集1跟3皆有出現Iraq,但是只有群集3有其他伊拉克戰爭相關的關鍵字,如:american,war和美國總統Bush,並且Iraq的次數也遠高於群集1的。

Which k-means cluster best corresponds to the democratic party? (Remember that we are looking for the names of the key democratic party leaders.)

  • 2
  • 這邊的關鍵字與Hierarchical分析一樣,有democrat,Edward,Kerry和Dean,因此群集2最有可能是民主黨。
2.3 ~ 2.6 兩種分群結果之間的對應關係

For the rest of this problem, we’ll ask you to compare how observations were assigned to clusters in the two different methods. Use the table function to compare the cluster assignment of hierarchical clustering to the cluster assignment of k-means clustering.

table(clustGroup,clustKM$cluster)
          
clustGroup    1    2    3    4    5    6    7
         1    3   11   64 1045   32    0  111
         2    0    0    0    0    0  320    1
         3   85   10   42   79  126    8   24
         4   10    5    0    0    1    0  123
         5   48    0  171  145    3    1   39
         6    0    2    0  712    0    0    0
         7    0  116    0   82    1    0   10

Which Hierarchical Cluster best corresponds to K-Means Cluster 2?

  • 7
  • 如同上述,cluster的order並沒有意義,從table可以看出,Hierarchical(row)的群集7對應到K means(column)的群集2是116,整排直行中最高並且過半。

Which Hierarchical Cluster best corresponds to K-Means Cluster 3?

  • 5
  • Hierarchical的群集5對應到K means的群集3是171,是最高且過半。

Which Hierarchical Cluster best corresponds to K-Means Cluster 7?

  • No Hierarchical Cluster contains at least half of the points in K-Means Cluster 7.
  • 單看K means的群集7,可以發現Hierarchical的群集1和4都非常高,但看總體比例卻都沒有過半,因此沒有一個與之對應的群集。

Which Hierarchical Cluster best corresponds to K-Means Cluster 6?

  • 2
  • Hierarchical的群集2對應到K means的群集6是320,幾乎是壟斷全部的比例,可見兩群集非常相似。
【討論問題】

字頻表是什麼?它的資料格式?

  • 字頻表是一種文字分析技術的產出,從多種來源蒐集(網路)文章、新聞、雜誌或評論,再將雜亂的資料進行整理和分析,最後統計出不同特定單詞出現的次數,以data frame的形式儲存,每一欄(column)代表不同的詞彙,而每一列(row)則存放該詞彙的出處。

使用字頻表作集群分析時,區隔變數是什麼?

  • 集群分析會以詞彙的不同來做分群,也就是所謂的區隔變數。
  • 區隔變數的值則代表該詞彙出現的次數,因此,將每一個集群做平均,即可得到該群中不同詞彙出現的頻率,並依此判斷集群的特性。

從樹狀圖判斷群數和從應用需求決定群數有什麼差別?

  • 樹狀圖會計算出點與點之間的距離(各點的差加總再開根號),並找出群內距離最小,且群間距離最大的分群方式,最後用樹狀圖呈現。
  • 樹狀圖中兩個集群間的垂直線愈長,代表集群到集群中心點之間的距離愈遠,這也是我們所偏好的;在垂直線上做水平切線,其切經過多少條垂直線,則代表有多少個集群。
  • 因此,跟從應用需求決定集群數目相反,我們在用樹狀圖的判斷方法時,剛開始不需考慮要分多少群,而是在機器幫我們做完運算之後,看圖來選擇要如何分群。








LS0tDQp0aXRsZTogIkFTNi0xIERhaWx5IEtvc+aWh+eroOWIhue+pCINCmF1dGhvcjogIuesrOS6lOe1hCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxicj4NCg0KKirkuLvopoHorbDpoYzvvJrkvp3lrZfpoLvooajlsI3mlofnq6DliIbnvqQqKg0KDQoqKuWtuOe/kumHjem7nu+8mioqDQoNCisg5L6d5a2X6aC76KGo5bCN5paH56ug5YiG576kDQorIOWxpOe0muW8j+mbhue+pOWIhuaekO+8mkhpZXJhcmNoaWNhbCBDbHVzdGVyIEFuYWx5c2lzDQorIOS+neaTmuaoueeLgOWcluaxuuWumuimgeWIhuWkmuWwkee+pA0KKyDkvp3mk5rmh4nnlKjmsbrlrpropoHliIblpJrlsJHnvqQNCisgSy1NZWFuc+mbhue+pOWIhuaekO+8mkstTWVhbnMgQ2x1c3RlciBBbmFseXNpcw0KKyDlvp7luLjopovlrZfovq3mjqjoq5bmlofpm4bnmoTkuLvpoYwNCg0KDQpgYGB7ciBlY2hvPVQsIG1lc3NhZ2U9RiwgY2FjaGU9Riwgd2FybmluZz1GfQ0Kcm0obGlzdD1scyhhbGw9VCkpDQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCJDIikNCm9wdGlvbnMoZGlnaXRzPTQsIHNjaXBlbj0xMikNCmxpYnJhcnkoZHBseXIpDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIDAuIOWQjeipnuino+mHiyjkuIroqrLnrYboqJgpDQoNCiMjIyMjIDEuMSDos4fmlpnmjqLli5gNCg0K5Ymb6ZaL5aeL5b6X5Yiw5LiA5Lu96LOH5paZ77yM6YKE5LiN5riF5qWa5pW05YCL6LOH5paZ55qE5qij6LKM77yM5rKS5pyJ55uu55qE5oCn55qE77yM5aSa5aSa5ZiX6Kmm5bCx5pyD55m854++5LiA5Lqb5pyJ6Laj55qE54++6LGh44CCRVguQVM2LTAgV2hvbGVzYWxlcywgTW92aWVzDQoNCiMjIyMjIDEuMiDliIbnvqTnrqHnkIYo5biC5aC05Y2A6ZqUKQ0KDQrngrrkuobopoHliIbnvqTnrqHnkIYo5pyJ55uu55qE5oCnKe+8jOWNgOmalOiuiuaVuOeahOmBuOaTh+acg+mBuOi3n+mCo+S6m+ebrueahOaciemXnOeahOS+huWBmuW4guWgtOWNgOmalOOAgkVYLkFTNi0yIEFpcmxpbmVzDQoNCiMjIyMjIDEuMyDlvaLmhYvlgbXmuKwo6aCQ5ris5oCn6Ki65pa3KQ0KDQrpgJnnqK7liIbnvqTmnIPliIblvpflvojntLDvvIzkvYbph43pu57kuI3mmK/lnKjliIbnvqTvvIzogIzmmK/ngrrkuobljrvmib7pgqPkupvlsJHmlbjlub7lgIvplbfnm7jnibnliKXjgIHmmI7poa/mnInnibnlvrXnmoTlsI/nvqQoRVguNjDlgIvoo6HpnaI1fjblgIvmupbnorrluqbpq5jmlrzlubPlnYcp77yM5Lim5ou/6YCZ5Lqb6aGv6JGX5LiN5ZCM55qE5bm+56iucGF0dGVybuWOu+WBmumgkOa4rOOAgg0KDQojIyMjIyAxLjQg5YiG576k6aCQ5risKOmbhue+pOWIhuaekOaooeWei+OAgeWIhue+pOmgkOa4rOaooeWeiykNCg0KKyDliIblroznvqTlho3lgZrpoJDmuKwoRVgu5b6I5aSa5bCP55a+55eF5pyD5bCO6Ie05b+D6Ief55eF77yM5L2G5q+P5YCL5q2356iL55qG5LiN5ZCMKeS7peWwiOalreefpeitmOWIpOaWt+S+huWIhue+pO+8jOWGjeS+huWBmumgkOa4rOOAgkVYLkFTNi0zIFN0b2Nrcw0KKyDmlbjpoZ7mqKHlnoso5Y+v5Lul55u05o6l5b6e5pW46YeP5LiK55yL5Ye66Zec5L+C55qE5qih5Z6LKeeahOWIhue+pOWPr+S7peS6pOe1puapn+WZqOWOu+iZleeQhu+8jOWboOeCuue0lOeyueS7peaVuOWtl+OAgee1seioiOmhr+iRl+aAp+S+huWIhue+pO+8jOapn+WZqOWBmueahOS4gOWumuavlOS6uuWlveOAgg0KKyDkvYboi6XpnIDopoHpnaDlsIjmpa3nn6XorZjkvobliKTmlrcoRVgu6IKh56Wo44CB6Yar55mCKe+8jOWwseimgeWFiOS7peS6uumhnuWwiOWutueahOWIpOaWt+WBmuWIhue+pO+8jOWGjee1puapn+WZqOWBmuaOpeS4i+S+hueahOmgkOa4rO+8jOebuOi8lOebuOS5mO+8jOmAmuW4uOmgkOa4rOS5n+acg+avlOi8g+a6lueiuuOAgg0KDQoNCiMjIyAxLiBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZw0KDQojIyMjIyAxLjEg5a2X6aC76KGo44CB6Led6Zui55+p6Zmj44CB6ZqO5bGk5byP6ZuG576k5YiG5p6QDQpMZXQncyBzdGFydCBieSBidWlsZGluZyBhIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG1vZGVsLiBGaXJzdCwgcmVhZCB0aGUgZGF0YSBzZXQgaW50byBSLiBUaGVuLCBjb21wdXRlIHRoZSBkaXN0YW5jZXMgKHVzaW5nIG1ldGhvZD0iZXVjbGlkZWFuIiksIGFuZCB1c2UgaGNsdXN0IHRvIGJ1aWxkIHRoZSBtb2RlbCAodXNpbmcgbWV0aG9kPSJ3YXJkLkQiKS4gWW91IHNob3VsZCBjbHVzdGVyIG9uIGFsbCBvZiB0aGUgdmFyaWFibGVzLg0KDQpgYGB7cn0NCkQgPSByZWFkLmNzdigiZGFpbHlrb3MuY3N2IikNCmRpbShEKQ0KYGBgDQoNCmBgYHtyfQ0KIyDlrZfpoLvooag6IERvY3VtZW50IFRlcm0gTWF0cml4DQpEWzE6MjAsIDE6MTBdDQpgYGANCg0KYGBge3J9DQojIOi3nembouefqemZozogRGlzdGFuY2UgTWF0cml4DQp0MCA9IFN5cy50aW1lKCkNCmRpc3QgPSBkaXN0KEQsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KU3lzLnRpbWUoKSAtIHQwDQpgYGANCl9SdW5uaW5nIHRoZSBkaXN0IGZ1bmN0aW9uIHdpbGwgcHJvYmFibHkgdGFrZSB5b3UgYSB3aGlsZS4gV2h5P18gU2VsZWN0IGFsbCB0aGF0IGFwcGx5Lg0KDQoNCmBgYHtyfQ0KIyDpmo7lsaTlvI/pm4bnvqTliIbmnpA6IEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIEFuYWx5c2lzDQp0MCA9IFN5cy50aW1lKCkNCmNsdXN0RCA9IGhjbHVzdChkaXN0LCBtZXRob2QgPSAid2FyZC5EIikNClN5cy50aW1lKCkgLSB0MA0KYGBgDQoNClBsb3QgdGhlIGRlbmRyb2dyYW0gb2YgeW91ciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtb2RlbC4gDQpgYGB7cn0NCnBsb3QoY2x1c3REKQ0KYGBgDQoNCiMjIyMjIDEuMiDlvp7mqLnni4DlnJbliKTmlrfnvqTmlbgNCkp1c3QgbG9va2luZyBhdCB0aGUgZGVuZHJvZ3JhbSwgDQoNCl93aGljaCBvZiB0aGUgZm9sbG93aW5nIHNlZW0gbGlrZSBnb29kIGNob2ljZXMgZm9yIHRoZSBudW1iZXIgb2YgY2x1c3RlcnM/XyBTZWxlY3QgYWxsIHRoYXQgYXBwbHkuDQoNCisgMg0KKyAzDQorIFRoZSBjaG9pY2VzIDIgYW5kIDMgYXJlIGdvb2QgY2x1c3RlciBjaG9pY2VzIGFjY29yZGluZyB0byB0aGUgZGVuZHJvZ3JhbSwgYmVjYXVzZSB0aGVyZSBpcyBhIGxvdCBvZiBzcGFjZSBiZXR3ZWVuIHRoZSBob3Jpem9udGFsIGxpbmVzIGluIHRoZSBkZW5kcm9ncmFtIGluIHRob3NlIGN1dCBvZmYgc3BvdHMuDQoNCiMjIyMjIDEuMyDlvp7mh4nnlKjmsbrlrprnvqTmlbgNCkluIHRoaXMgcHJvYmxlbSwgd2UgYXJlIHRyeWluZyB0byBjbHVzdGVyIG5ld3MgYXJ0aWNsZXMgb3IgYmxvZyBwb3N0cyBpbnRvIGdyb3Vwcy4gVGhpcyBjYW4gYmUgdXNlZCB0byBzaG93IHJlYWRlcnMgY2F0ZWdvcmllcyB0byBjaG9vc2UgZnJvbSB3aGVuIHRyeWluZyB0byBkZWNpZGUgd2hhdCB0byByZWFkLiBKdXN0IHRoaW5raW5nIGFib3V0IHRoaXMgYXBwbGljYXRpb24sIA0KDQpfd2hhdCBhcmUgZ29vZCBjaG9pY2VzIGZvciB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzP18gU2VsZWN0IGFsbCB0aGF0IGFwcGx5Lg0KDQorIDcNCisgOA0KKyBJdCBpcyBwcm9iYWJseSBiZXR0ZXIgdG8gc2hvdyB0aGUgcmVhZGVyIG1vcmUgY2F0ZWdvcmllcyB0aGFuIDIgb3IgMy4gVGhlc2UgY2F0ZWdvcmllcyB3b3VsZCBwcm9iYWJseSBiZSB0b28gYnJvYWQgdG8gYmUgdXNlZnVsLiBTZXZlbiBvciBlaWdodCBjYXRlZ29yaWVzIHNlZW1zIG1vcmUgcmVhc29uYWJsZS4NCg0KIyMjIyMgMS40IOS+nee+pOe1hOWIhuWJsuizh+aWmQ0KTGV0J3MgcGljayA3IGNsdXN0ZXJzLiBUaGlzIG51bWJlciBpcyByZWFzb25hYmxlIGFjY29yZGluZyB0byB0aGUgZGVuZHJvZ3JhbSwgYW5kIGFsc28gc2VlbXMgcmVhc29uYWJsZSBmb3IgdGhlIGFwcGxpY2F0aW9uLiBVc2UgdGhlIGN1dHJlZSBmdW5jdGlvbiB0byBzcGxpdCB5b3VyIGRhdGEgaW50byA3IGNsdXN0ZXJzLg0KYGBge3J9DQpjbHVzdEdyb3VwID0gY3V0cmVlKGNsdXN0RCwgaz03KQ0KY2x1c3QxID0gc3Vic2V0KEQsIGNsdXN0R3JvdXA9PTEpICMgdGhlIG1vc3Qgb2JzLg0KY2x1c3QyID0gc3Vic2V0KEQsIGNsdXN0R3JvdXA9PTIpDQpjbHVzdDMgPSBzdWJzZXQoRCwgY2x1c3RHcm91cD09MykgIyAzNzQgb2JzLg0KY2x1c3Q0ID0gc3Vic2V0KEQsIGNsdXN0R3JvdXA9PTQpICMgdGhlIGZld2VzdCBvYnMuDQpjbHVzdDUgPSBzdWJzZXQoRCwgY2x1c3RHcm91cD09NSkNCmNsdXN0NiA9IHN1YnNldChELCBjbHVzdEdyb3VwPT02KQ0KY2x1c3Q3ID0gc3Vic2V0KEQsIGNsdXN0R3JvdXA9PTcpDQpjbHVzdEhpID0gc3BsaXQoRCwgY2x1c3RHcm91cCkgIyDkuZ/lj6/ku6XlsIfku6XkuIpjb2Rl57Ch5YyW77yM55So5q2k6KGM5L6G55u05o6l5YiG576kDQpgYGANCg0KTm93LCB3ZSBkb24ndCByZWFsbHkgd2FudCB0byBydW4gdGFwcGx5IG9uIGV2ZXJ5IHNpbmdsZSB2YXJpYWJsZSB3aGVuIHdlIGhhdmUgb3ZlciAxLDAwMCBkaWZmZXJlbnQgdmFyaWFibGVzLiBMZXQncyBpbnN0ZWFkIHVzZSB0aGUgc3Vic2V0IGZ1bmN0aW9uIHRvIHN1YnNldCBvdXIgZGF0YSBieSBjbHVzdGVyLiBDcmVhdGUgNyBuZXcgZGF0YXNldHMsIGVhY2ggY29udGFpbmluZyB0aGUgb2JzZXJ2YXRpb25zIGZyb20gb25lIG9mIHRoZSBjbHVzdGVycy4NCg0KX0hvdyBtYW55IG9ic2VydmF0aW9ucyBhcmUgaW4gY2x1c3RlciAzP18NCmBgYHtyfQ0KbnJvdyhjbHVzdDMpICMgMzc0IG9icy4NCmBgYA0KDQpgYGB7cn0NClZpZXcoY2x1c3RIaSkgIyDmiJbmmK/lvp7nkrDlooPorormlbjnm7TmjqXnnIvmr4/lgIvnvqTpm4bmnInlub7lgIvorormlbgNCmBgYA0KDQpfV2hpY2ggY2x1c3RlciBoYXMgdGhlIG1vc3Qgb2JzZXJ2YXRpb25zP18NCg0KKyAxDQoNCl9XaGljaCBjbHVzdGVyIGhhcyB0aGUgZmV3ZXN0IG9ic2VydmF0aW9ucz9fDQoNCisgNA0KDQojIyMjIyAxLjUg5om+5Ye656ys5LiA5peP576k5Lit5pyA5bi46KaL55qE5a2X6L6tDQpJbnN0ZWFkIG9mIGxvb2tpbmcgYXQgdGhlIGF2ZXJhZ2UgdmFsdWUgaW4gZWFjaCB2YXJpYWJsZSBpbmRpdmlkdWFsbHksIHdlJ2xsIGp1c3QgbG9vayBhdCB0aGUgdG9wIDYgd29yZHMgaW4gZWFjaCBjbHVzdGVyLiBUbyBkbyB0aGlzIGZvciBjbHVzdGVyIDEsIHR5cGUgdGhlIGZvbGxvd2luZyBpbiB5b3VyIFIgY29uc29sZSAod2hlcmUgIkhpZXJDbHVzdGVyMSIgc2hvdWxkIGJlIHJlcGxhY2VkIHdpdGggdGhlIG5hbWUgb2YgeW91ciBmaXJzdCBjbHVzdGVyIHN1YnNldCk6DQoNCnRhaWwoc29ydChjb2xNZWFucyhIaWVyQ2x1c3RlcjEpKSkNCg0KVGhpcyBjb21wdXRlcyB0aGUgbWVhbiBmcmVxdWVuY3kgdmFsdWVzIG9mIGVhY2ggb2YgdGhlIHdvcmRzIGluIGNsdXN0ZXIgMSwgYW5kIHRoZW4gb3V0cHV0cyB0aGUgNiB3b3JkcyB0aGF0IG9jY3VyIHRoZSBtb3N0IGZyZXF1ZW50bHkuIFRoZSBjb2xNZWFucyBmdW5jdGlvbiBjb21wdXRlcyB0aGUgY29sdW1uICh3b3JkKSBtZWFucywgdGhlIHNvcnQgZnVuY3Rpb24gb3JkZXJzIHRoZSB3b3JkcyBpbiBpbmNyZWFzaW5nIG9yZGVyIG9mIHRoZSBtZWFuIHZhbHVlcywgYW5kIHRoZSB0YWlsIGZ1bmN0aW9uIG91dHB1dHMgdGhlIGxhc3QgNiB3b3JkcyBsaXN0ZWQsIHdoaWNoIGFyZSB0aGUgb25lcyB3aXRoIHRoZSBsYXJnZXN0IGNvbHVtbiBtZWFucy4NCg0KX1doYXQgaXMgdGhlIG1vc3QgZnJlcXVlbnQgd29yZCBpbiB0aGlzIGNsdXN0ZXIsIGluIHRlcm1zIG9mIGF2ZXJhZ2UgdmFsdWU/XyBFbnRlciB0aGUgd29yZCBleGFjdGx5IGhvdyB5b3Ugc2VlIGl0IGluIHRoZSBvdXRwdXQ6DQpgYGB7cn0NCnRhaWwoc29ydChjb2xNZWFucyhjbHVzdDEpKSkgIyBidXNoDQojIGNvbE1lYW5z5piv5Lul5qyEKOmAmeijoeeahOavj+WAi+ashOS9jeiuiuaVuOmDveaYr+S4gOWAi3dvcmQp54K65Zau5L2N5L6G6KiI566X5bmz5Z2H5YC877yM5bCx5q2k5L6L6aGM77yM5qyE5L2N55qE5bmz5Z2H5YC85Luj6KGo6Kmyd29yZOWHuuePvueahOavlOeOh+OAgg0KYGBgDQpUaGlzIGNvbXB1dGVzIHRoZSBtZWFuIGZyZXF1ZW5jeSB2YWx1ZXMgb2YgZWFjaCBvZiB0aGUgd29yZHMgaW4gY2x1c3RlciAxLCBhbmQgdGhlbiBvdXRwdXRzIHRoZSA2IHdvcmRzIHRoYXQgb2NjdXIgdGhlIG1vc3QgZnJlcXVlbnRseS4NCg0KIyMjIyMgMS42IOaJvuWHuuWQhOaXj+e+pOS4reacgOW4uOimi+eahOWtl+i+rQ0KTm93IHJlcGVhdCB0aGUgY29tbWFuZCBnaXZlbiBpbiB0aGUgcHJldmlvdXMgcHJvYmxlbSBmb3IgZWFjaCBvZiB0aGUgb3RoZXIgY2x1c3RlcnMsIGFuZCBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuDQpgYGB7cn0NCnRhaWwoc29ydChjb2xNZWFucyhjbHVzdDIpKSkgDQp0YWlsKHNvcnQoY29sTWVhbnMoY2x1c3QzKSkpIA0KdGFpbChzb3J0KGNvbE1lYW5zKGNsdXN0NCkpKSANCnRhaWwoc29ydChjb2xNZWFucyhjbHVzdDUpKSkgDQp0YWlsKHNvcnQoY29sTWVhbnMoY2x1c3Q2KSkpDQp0YWlsKHNvcnQoY29sTWVhbnMoY2x1c3Q3KSkpDQpgYGANCg0KX1doaWNoIHdvcmRzIGJlc3QgZGVzY3JpYmUgY2x1c3RlciAyP18NCg0KKyBub3ZlbWJlciwgcG9sbCwgdm90ZQ0KKyDnlLHmraTlj6/nnIvlh7rnvqTpm4Yy55qE5Lq677yM6KiO6KuW6Kix5aSa6Zec5pa8MTHmnIjmmYLnmoTpgbjoiInjgIINCg0KX1doaWNoIGNsdXN0ZXIgY291bGQgYmVzdCBiZSBkZXNjcmliZWQgYXMgdGhlIGNsdXN0ZXIgcmVsYXRlZCB0byB0aGUgSXJhcSB3YXI/Xw0KDQorIDUNCisgSXJhceeahOmXnOmNteWtl+WPquWHuuePvuWcqOe+pOmbhjXvvIzkuKbkuJTpq5jpgZQyLjQyOO+8jOWFtuS7lueahOmXnOmNteWtl+aciXdhciznvo7lnIvnuL3ntbFCdXNo5ZKMQW1lcmljYW7vvIzkuZ/lj43mh4nlh7rot5/kvIrmi4nlhYvmiLDniK3nibnliKXnm7jpl5zjgIINCg0KSW4gMjAwNCwgb25lIG9mIHRoZSBjYW5kaWRhdGVzIGZvciB0aGUgRGVtb2NyYXRpYyBub21pbmF0aW9uIGZvciB0aGUgUHJlc2lkZW50IG9mIHRoZSBVbml0ZWQgU3RhdGVzIHdhcyBIb3dhcmQgRGVhbiwgSm9obiBLZXJyeSB3YXMgdGhlIGNhbmRpZGF0ZSB3aG8gd29uIHRoZSBkZW1vY3JhdGljIG5vbWluYXRpb24sIGFuZCBKb2huIEVkd2FyZHMgd2l0aCB0aGUgcnVubmluZyBtYXRlIG9mIEpvaG4gS2VycnkgKHRoZSBWaWNlIFByZXNpZGVudCBub21pbmVlKS4gR2l2ZW4gdGhpcyBpbmZvcm1hdGlvbiwgDQoNCl93aGljaCBjbHVzdGVyIGJlc3QgY29ycmVzcG9uZHMgdG8gdGhlIGRlbW9jcmF0aWMgcGFydHk/Xw0KDQorIDcNCisg576k6ZuGN+mZpOS6huaciWRlbW9jcmF06Zec6Y215a2X5Lul5aSWLEVkd2FyZCxLZXJyeeWSjERlYW7pg73mmK/msJHkuLvpu6jnmoTpgbjoiInkurrku6PooajmiJbmlK/mjIHogIXvvIzlm6DmraTljbPkvb9kZW1vY3JhdOacrOi6q+WHuuePvueahOasoeaVuOavlOWFtuS7lue+pOmbhuS4remChOWwke+8jOeUseaWvOaVtOWAi+e+pOmbhumDveaYr+eUseawkeS4u+m7qOebuOmXnOeahOaIkOWIhuaJgOe1hOaIkO+8jOWboOatpOacgOacieWPr+iDveaYr+awkeS4u+m7qOOAgg0KDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyAyIEstTWVhbnMgQ2x1c3RlcmluZw0KDQojIyMjIyAyLjEgSy1NZWFuc+mbhue+pOWIhuaekA0KTm93LCBydW4gay1tZWFucyBjbHVzdGVyaW5nLCBzZXR0aW5nIHRoZSBzZWVkIHRvIDEwMDAgcmlnaHQgYmVmb3JlIHlvdSBydW4gdGhlIGttZWFucyBmdW5jdGlvbi4gQWdhaW4sIHBpY2sgdGhlIG51bWJlciBvZiBjbHVzdGVycyBlcXVhbCB0byA3LiBZb3UgZG9uJ3QgbmVlZCB0byBhZGQgdGhlIGl0ZXJzLm1heCBhcmd1bWVudC4NCmBgYHtyfQ0KbGlicmFyeShjYVRvb2xzKQ0Kc2V0LnNlZWQoMTAwMCkNCmNsdXN0S00gPSBrbWVhbnMoRCwgY2VudGVycz03KQ0KY2x1c3RLTWdyb3VwID0gc3BsaXQoRCwgY2x1c3RLTSRjbHVzdGVyKQ0KbnJvdyhjbHVzdEtNW1szXV0pDQpWaWV3KGNsdXN0S00pDQojIGNsdXN0ZXI0IGhhcyB0aGUgbW9zdCBvYnMsIHdoaWxlIGNsdXN0ZXIyIGhhcyB0aGUgZmV3ZXN0IG9icy4NCmBgYA0KDQpTdWJzZXQgeW91ciBkYXRhIGludG8gdGhlIDcgY2x1c3RlcnMgKDcgbmV3IGRhdGFzZXRzKSBieSB1c2luZyB0aGUgImNsdXN0ZXIiIHZhcmlhYmxlIG9mIHlvdXIga21lYW5zIG91dHB1dC4NCg0KX0hvdyBtYW55IG9ic2VydmF0aW9ucyBhcmUgaW4gQ2x1c3RlciAzP18NCg0KKyAyNzcNCisg55Sx5pa8SyBtZWFuc+eahOWIhue+pOaWueW8j+i3n0hpZXJhcmNoaWNhbOS4jeWQjO+8jOW+l+WHuuWQhOWAi+e+pOmbhuS4reeahG9ic+WAvOS5n+iHqueEtuS4jeWQjO+8jOS4puS4lOaJgOaciee+pOmbhueahOmghuW6jyhjbHVzdGVyIDF+NynmmK/mspLmnInmhI/nvqnnmoTvvIzlj6rmmK/kuIDlgIvku6PomZ/jgIINCg0KX1doaWNoIGNsdXN0ZXIgaGFzIHRoZSBtb3N0IG9ic2VydmF0aW9ucz9fDQoNCisgNA0KDQpfV2hpY2ggY2x1c3RlciBoYXMgdGhlIGZld2VzdCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zP18NCg0KKyAyDQoNCiMjIyMjIDIuMiDmib7lh7rlkITml4/nvqTkuK3mnIDluLjopovnmoTlrZfovq0NCk5vdywgb3V0cHV0IHRoZSBzaXggbW9zdCBmcmVxdWVudCB3b3JkcyBpbiBlYWNoIGNsdXN0ZXIsIGxpa2Ugd2UgZGlkIGluIHRoZSBwcmV2aW91cyBwcm9ibGVtLCBmb3IgZWFjaCBvZiB0aGUgay1tZWFucyBjbHVzdGVycy4NCmBgYHtyfQ0KdGFpbChzb3J0KGNvbE1lYW5zKGNsdXN0S01ncm91cFtbMV1dKSkpDQp0YWlsKHNvcnQoY29sTWVhbnMoY2x1c3RLTWdyb3VwW1syXV0pKSkNCnRhaWwoc29ydChjb2xNZWFucyhjbHVzdEtNZ3JvdXBbWzNdXSkpKQ0KdGFpbChzb3J0KGNvbE1lYW5zKGNsdXN0S01ncm91cFtbNF1dKSkpDQp0YWlsKHNvcnQoY29sTWVhbnMoY2x1c3RLTWdyb3VwW1s1XV0pKSkNCnRhaWwoc29ydChjb2xNZWFucyhjbHVzdEtNZ3JvdXBbWzZdXSkpKQ0KdGFpbChzb3J0KGNvbE1lYW5zKGNsdXN0S01ncm91cFtbN11dKSkpDQpgYGANCg0KX1doaWNoIGstbWVhbnMgY2x1c3RlciBiZXN0IGNvcnJlc3BvbmRzIHRvIHRoZSBJcmFxIFdhcj9fDQoNCisgMw0KKyDpm5bnhLbnvqTpm4Yx6LefM+eahuacieWHuuePvklyYXHvvIzkvYbmmK/lj6rmnInnvqTpm4Yz5pyJ5YW25LuW5LyK5ouJ5YWL5oiw54it55u46Zec55qE6Zec6Y215a2X77yM5aaCOmFtZXJpY2FuLHdhcuWSjOe+juWci+e4vee1sUJ1c2jvvIzkuKbkuJRJcmFx55qE5qyh5pW45Lmf6YGg6auY5pa8576k6ZuGMeeahOOAgg0KDQpfV2hpY2ggay1tZWFucyBjbHVzdGVyIGJlc3QgY29ycmVzcG9uZHMgdG8gdGhlIGRlbW9jcmF0aWMgcGFydHk/XyAoUmVtZW1iZXIgdGhhdCB3ZSBhcmUgbG9va2luZyBmb3IgdGhlIG5hbWVzIG9mIHRoZSBrZXkgZGVtb2NyYXRpYyBwYXJ0eSBsZWFkZXJzLikNCg0KKyAyDQorIOmAmemCiueahOmXnOmNteWtl+iIh0hpZXJhcmNoaWNhbOWIhuaekOS4gOaoo++8jOaciWRlbW9jcmF0LEVkd2FyZCxLZXJyeeWSjERlYW7vvIzlm6DmraTnvqTpm4Yy5pyA5pyJ5Y+v6IO95piv5rCR5Li76buo44CCDQoNCiMjIyMjIDIuMyB+IDIuNiDlhannqK7liIbnvqTntZDmnpzkuYvplpPnmoTlsI3mh4npl5zkv4INCkZvciB0aGUgcmVzdCBvZiB0aGlzIHByb2JsZW0sIHdlJ2xsIGFzayB5b3UgdG8gY29tcGFyZSBob3cgb2JzZXJ2YXRpb25zIHdlcmUgYXNzaWduZWQgdG8gY2x1c3RlcnMgaW4gdGhlIHR3byBkaWZmZXJlbnQgbWV0aG9kcy4gVXNlIHRoZSB0YWJsZSBmdW5jdGlvbiB0byBjb21wYXJlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgb2YgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBvZiBrLW1lYW5zIGNsdXN0ZXJpbmcuDQpgYGB7cn0NCnRhYmxlKGNsdXN0R3JvdXAsY2x1c3RLTSRjbHVzdGVyKQ0KYGBgDQpfV2hpY2ggSGllcmFyY2hpY2FsIENsdXN0ZXIgYmVzdCBjb3JyZXNwb25kcyB0byBLLU1lYW5zIENsdXN0ZXIgMj9fDQoNCisgNw0KKyDlpoLlkIzkuIrov7DvvIxjbHVzdGVy55qEb3JkZXLkuKbmspLmnInmhI/nvqnvvIzlvp50YWJsZeWPr+S7peeci+WHuu+8jEhpZXJhcmNoaWNhbChyb3cp55qE576k6ZuGN+WwjeaHieWIsEsgbWVhbnMoY29sdW1uKeeahOe+pOmbhjLmmK8xMTbvvIzmlbTmjpLnm7TooYzkuK3mnIDpq5jkuKbkuJTpgY7ljYrjgIINCg0KX1doaWNoIEhpZXJhcmNoaWNhbCBDbHVzdGVyIGJlc3QgY29ycmVzcG9uZHMgdG8gSy1NZWFucyBDbHVzdGVyIDM/Xw0KDQorIDUNCisgSGllcmFyY2hpY2Fs55qE576k6ZuGNeWwjeaHieWIsEsgbWVhbnPnmoTnvqTpm4Yz5pivMTcx77yM5piv5pyA6auY5LiU6YGO5Y2K44CCDQoNCl9XaGljaCBIaWVyYXJjaGljYWwgQ2x1c3RlciBiZXN0IGNvcnJlc3BvbmRzIHRvIEstTWVhbnMgQ2x1c3RlciA3P18NCg0KKyBObyBIaWVyYXJjaGljYWwgQ2x1c3RlciBjb250YWlucyBhdCBsZWFzdCBoYWxmIG9mIHRoZSBwb2ludHMgaW4gSy1NZWFucyBDbHVzdGVyIDcuDQorIOWWrueci0sgbWVhbnPnmoTnvqTpm4Y377yM5Y+v5Lul55m854++SGllcmFyY2hpY2Fs55qE576k6ZuGMeWSjDTpg73pnZ7luLjpq5jvvIzkvYbnnIvnuL3pq5Tmr5Tkvovljbvpg73mspLmnInpgY7ljYrvvIzlm6DmraTmspLmnInkuIDlgIvoiIfkuYvlsI3mh4nnmoTnvqTpm4bjgIINCg0KX1doaWNoIEhpZXJhcmNoaWNhbCBDbHVzdGVyIGJlc3QgY29ycmVzcG9uZHMgdG8gSy1NZWFucyBDbHVzdGVyIDY/Xw0KDQorIDINCisgSGllcmFyY2hpY2Fs55qE576k6ZuGMuWwjeaHieWIsEsgbWVhbnPnmoTnvqTpm4Y25pivMzIw77yM5bm+5LmO5piv5aOf5pa35YWo6YOo55qE5q+U5L6L77yM5Y+v6KaL5YWp576k6ZuG6Z2e5bi455u45Ly844CCDQoNCiMjIyMjIOOAkOiojuirluWVj+mhjOOAkQ0KDQrlrZfpoLvooajmmK/ku4DpurzvvJ/lroPnmoTos4fmlpnmoLzlvI/vvJ8NCg0KKyDlrZfpoLvooajmmK/kuIDnqK7mloflrZfliIbmnpDmioDooZPnmoTnlKLlh7rvvIzlvp7lpJrnqK7kvobmupDokpDpm4Yo57ay6LevKeaWh+eroOOAgeaWsOiBnuOAgembnOiqjOaIluipleirlu+8jOWGjeWwh+mbnOS6gueahOizh+aWmemAsuihjOaVtOeQhuWSjOWIhuaekO+8jOacgOW+jOe1seioiOWHuuS4jeWQjOeJueWumuWWruipnuWHuuePvueahOasoeaVuO+8jOS7pWRhdGEgZnJhbWXnmoTlvaLlvI/lhLLlrZjvvIzmr4/kuIDmrIQoY29sdW1uKeS7o+ihqOS4jeWQjOeahOipnuW9me+8jOiAjOavj+S4gOWIlyhyb3cp5YmH5a2Y5pS+6Kmy6Kme5b2Z55qE5Ye66JmV44CCDQoNCuS9v+eUqOWtl+mgu+ihqOS9nOmbhue+pOWIhuaekOaZgu+8jOWNgOmalOiuiuaVuOaYr+S7gOm6vO+8nw0KDQorIOmbhue+pOWIhuaekOacg+S7peipnuW9meeahOS4jeWQjOS+huWBmuWIhue+pO+8jOS5n+WwseaYr+aJgOisgueahOWNgOmalOiuiuaVuOOAgg0KKyDljYDpmpTorormlbjnmoTlgLzliYfku6PooajoqbLoqZ7lvZnlh7rnj77nmoTmrKHmlbjvvIzlm6DmraTvvIzlsIfmr4/kuIDlgIvpm4bnvqTlgZrlubPlnYfvvIzljbPlj6/lvpfliLDoqbLnvqTkuK3kuI3lkIzoqZ7lvZnlh7rnj77nmoTpoLvnjofvvIzkuKbkvp3mraTliKTmlrfpm4bnvqTnmoTnibnmgKfjgIINCg0K5b6e5qi554uA5ZyW5Yik5pa3576k5pW45ZKM5b6e5oeJ55So6ZyA5rGC5rG65a6a576k5pW45pyJ5LuA6bq85beu5Yil77yfDQoNCisg5qi554uA5ZyW5pyD6KiI566X5Ye66bue6IiH6bue5LmL6ZaT55qE6Led6ZuiKOWQhOm7nueahOW3ruWKoOe4veWGjemWi+agueiZnynvvIzkuKbmib7lh7rnvqTlhafot53pm6LmnIDlsI/vvIzkuJTnvqTplpPot53pm6LmnIDlpKfnmoTliIbnvqTmlrnlvI/vvIzmnIDlvoznlKjmqLnni4DlnJblkYjnj77jgIINCisg5qi554uA5ZyW5Lit5YWp5YCL6ZuG576k6ZaT55qE5Z6C55u057ea5oSI6ZW377yM5Luj6KGo6ZuG576k5Yiw6ZuG576k5Lit5b+D6bue5LmL6ZaT55qE6Led6Zui5oSI6YGg77yM6YCZ5Lmf5piv5oiR5YCR5omA5YGP5aW955qE77yb5Zyo5Z6C55u057ea5LiK5YGa5rC05bmz5YiH57ea77yM5YW25YiH57aT6YGO5aSa5bCR5qKd5Z6C55u057ea77yM5YmH5Luj6KGo5pyJ5aSa5bCR5YCL6ZuG576k44CCDQorIOWboOatpO+8jOi3n+W+nuaHieeUqOmcgOaxguaxuuWumumbhue+pOaVuOebruebuOWPje+8jOaIkeWAkeWcqOeUqOaoueeLgOWclueahOWIpOaWt+aWueazleaZgu+8jOWJm+mWi+Wni+S4jemcgOiAg+aFruimgeWIhuWkmuWwkee+pO+8jOiAjOaYr+WcqOapn+WZqOW5q+aIkeWAkeWBmuWujOmBi+eul+S5i+W+jO+8jOeci+WcluS+humBuOaTh+imgeWmguS9leWIhue+pOOAgg0KDQo8YnI+DQoNCi0gLSAtDQoNCjxicj48YnI+PGJyPjxicj48YnI+DQoNCjxzdHlsZT4NCi5jYXB0aW9uIHsNCiAgY29sb3I6ICM3Nzc7DQogIG1hcmdpbi10b3A6IDEwcHg7DQp9DQpwIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnByZSB7DQogIHdvcmQtYnJlYWs6IG5vcm1hbDsNCiAgd29yZC13cmFwOiBub3JtYWw7DQogIGxpbmUtaGVpZ2h0OiAxOw0KfQ0KcHJlIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KLnJ7DQogIGxpbmUtaGVpZ2h0OiAxLjI7DQp9DQoNCnRpdGxlew0KICBjb2xvcjogI2NjMDAwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmJvZHl7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoMSxoMixoMyxoNCxoNXsNCiAgY29sb3I6ICMwMDg4MDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoM3sNCiAgY29sb3I6ICNiMzZiMDA7DQogIGJhY2tncm91bmQ6ICNmZmUwYjM7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDV7DQogIGNvbG9yOiAjMDA2MDAwOw0KICBiYWNrZ3JvdW5kOiAjZmZmZmUwOw0KICBsaW5lLWhlaWdodDogMjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmVtew0KICBjb2xvcjogIzAwMDBjMDsNCiAgYmFja2dyb3VuZDogI2YwZjBmMDsNCiAgfQ0KDQo8L3N0eWxlPg0KDQo=