主要議題:依顧客屬性做市場區隔

學習重點:

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



1. 資料常態化

1.1 資料摘要

Read the dataset AirlinesCluster.csv into R and call it “airlines”. Looking at the summary of airlines,

getwd()
[1] "C:/MIT summer 2018/Unit6"
airlines = read.csv("data/AirlinesCluster.csv")
summary(airlines)
    Balance          QualMiles       BonusMiles       BonusTrans    FlightMiles     FlightTrans   
 Min.   :      0   Min.   :    0   Min.   :     0   Min.   : 0.0   Min.   :    0   Min.   : 0.00  
 1st Qu.:  18528   1st Qu.:    0   1st Qu.:  1250   1st Qu.: 3.0   1st Qu.:    0   1st Qu.: 0.00  
 Median :  43097   Median :    0   Median :  7171   Median :12.0   Median :    0   Median : 0.00  
 Mean   :  73601   Mean   :  144   Mean   : 17145   Mean   :11.6   Mean   :  460   Mean   : 1.37  
 3rd Qu.:  92404   3rd Qu.:    0   3rd Qu.: 23800   3rd Qu.:17.0   3rd Qu.:  311   3rd Qu.: 1.00  
 Max.   :1704838   Max.   :11148   Max.   :263685   Max.   :86.0   Max.   :30817   Max.   :53.00  
 DaysSinceEnroll
 Min.   :   2   
 1st Qu.:2330   
 Median :4096   
 Mean   :4119   
 3rd Qu.:5790   
 Max.   :8296   

which TWO variables have (on average) the smallest values?

  • BonusTrans
  • FlightTrans

Which TWO variables have (on average) the largest values?

  • Balance
  • BonusMiles
1.2 為甚麼要做資料常態化

In this problem, we will normalize our data before we run the clustering algorithms.

Why is it important to normalize the data before clustering?

  • If we don’t normalize the data, the clustering will be dominated by the variables that are on a larger scale.
1.3 使用caret套件做資料常態化

Let’s go ahead and normalize our data. You can normalize the variables in a data frame by using the preProcess function in the “caret” package. You should already have this package installed from Week 4, but if not, go ahead and install it with install.packages(“caret”). Then load the package with library(caret).

Now, create a normalized data frame called “airlinesNorm” by running the following commands:

preproc = preProcess(airlines)

airlinesNorm = predict(preproc, airlines)

The first command pre-processes the data, and the second command performs the normalization. If you look at the summary of airlinesNorm, you should see that all of the variables now have mean zero. You can also see that each of the variables has standard deviation 1 by using the sd() function.

library(caret)
preproc = preProcess(airlines)
airlinesNorm = predict(preproc, airlines)
summary(airlinesNorm)
    Balance         QualMiles        BonusMiles       BonusTrans      FlightMiles    
 Min.   :-0.730   Min.   :-0.186   Min.   :-0.710   Min.   :-1.208   Min.   :-0.329  
 1st Qu.:-0.546   1st Qu.:-0.186   1st Qu.:-0.658   1st Qu.:-0.896   1st Qu.:-0.329  
 Median :-0.303   Median :-0.186   Median :-0.413   Median : 0.041   Median :-0.329  
 Mean   : 0.000   Mean   : 0.000   Mean   : 0.000   Mean   : 0.000   Mean   : 0.000  
 3rd Qu.: 0.187   3rd Qu.:-0.186   3rd Qu.: 0.276   3rd Qu.: 0.562   3rd Qu.:-0.106  
 Max.   :16.187   Max.   :14.223   Max.   :10.208   Max.   : 7.747   Max.   :21.680  
  FlightTrans     DaysSinceEnroll  
 Min.   :-0.362   Min.   :-1.9934  
 1st Qu.:-0.362   1st Qu.:-0.8661  
 Median :-0.362   Median :-0.0109  
 Mean   : 0.000   Mean   : 0.0000  
 3rd Qu.:-0.098   3rd Qu.: 0.8096  
 Max.   :13.610   Max.   : 2.0228  

In the normalized data, which variable has the largest maximum value?

  • FlightMiles

In the normalized data, which variable has the smallest minimum value?

  • DaysSinceEnroll



2. 層級式集群分析

2.1 依據樹狀圖和應用需求決定群數

Compute the distances between data points (using euclidean distance) and then run the Hierarchical clustering algorithm (using method=“ward.D”) on the normalized data. It may take a few minutes for the commands to finish since the dataset has a large number of observations for hierarchical clustering.

Then, plot the dendrogram of the hierarchical clustering process. Suppose the airline is looking for somewhere between 2 and 10 clusters.

distance = dist(airlinesNorm,method = "euclidean")
clusterairlines = hclust(distance,method="ward.D")
plot(clusterairlines)

According to the dendrogram, which of the following is NOT a good choice for the number of clusters?

  • 6
2.2 分割群組

Suppose that after looking at the dendrogram and discussing with the marketing department, the airline decides to proceed with 5 clusters. Divide the data points into 5 clusters by using the cutree function.

hierGroups = cutree(clusterairlines, k = 5)
table(hierGroups)
hierGroups
   1    2    3    4    5 
 776  519  494  868 1342 

How many data points are in Cluster 1?

  • 776
2.3 從區隔變數的平均值推論族群特性

Now, use tapply to compare the average values in each of the variables for the 5 clusters (the centroids of the clusters). You may want to compute the average values of the unnormalized data so that it is easier to interpret. You can do this for the variable “Balance” with the following command:

tapply(airlines$Balance, clusterGroups, mean)

names(airlines)
[1] "Balance"         "QualMiles"       "BonusMiles"      "BonusTrans"      "FlightMiles"    
[6] "FlightTrans"     "DaysSinceEnroll"
airlines$hierGroups = hierGroups
sapply(split(airlines[,1:7], hierGroups), colMeans)
                         1          2          3          4          5
Balance         57866.9046 110669.266 198191.575 52335.9136 36255.9098
QualMiles           0.6443   1065.983     30.346     4.8479     2.5112
BonusMiles      10360.1237  22881.763  55795.860 20788.7661  2264.7876
BonusTrans         10.8235     18.229     19.664    17.0876     2.9732
FlightMiles        83.1843   2613.418    327.676   111.5737   119.3219
FlightTrans         0.3028      7.403      1.069     0.3445     0.4389
DaysSinceEnroll  6235.3647   4402.414   5615.709  2840.8226  3060.0812

Compared to the other clusters, Cluster 1 has the largest average values in which variables (if any)? Select all that apply.

  • DaysSinceEnroll

How would you describe the customers in Cluster 1?

  • Infrequent but loyal customers.
2.4 Cluster 2

Compared to the other clusters, Cluster 2 has the largest average values in which variables (if any)? Select all that apply.

  • QualMiles
  • FlightMiles
  • FlightTrans

How would you describe the customers in Cluster 2?

  • Customers who have accumulated a large amount of miles, and the ones with the largest number of flight transactions.
2.5 Cluster 3

Compared to the other clusters, Cluster 3 has the largest average values in which variables (if any)? Select all that apply.

  • Balance
  • BonusMiles
  • BonusTrans

How would you describe the customers in Cluster 3?

  • Customers who have accumulated a large amount of miles, mostly through non-flight transactions. 正确
2.6 Cluster 4

Compared to the other clusters, Cluster 4 has the largest average values in which variables (if any)? Select all that apply.

  • None

How would you describe the customers in Cluster 4?

  • Relatively new customers who seem to be accumulating miles, mostly through non-flight transactions.
2.7 Cluster 5

Compared to the other clusters, Cluster 5 has the largest average values in which variables (if any)? Select all that apply.

  • None

How would you describe the customers in Cluster 5?

  • Relatively new customers who don’t use the airline very often.

3. K-Means集群分析

3.1 K-Means集群分析

Now run the k-means clustering algorithm on the normalized data, again creating 5 clusters. Set the seed to 88 right before running the clustering algorithm, and set the argument iter.max to 1000.

set.seed(88)
KMC = kmeans(airlinesNorm, centers = 5,iter.max =1000 )
table(KMC$cluster)

   1    2    3    4    5 
 408  141  993 1182 1275 

How many clusters have more than 1,000 observations?

  • 2
3.2 Hierarchical和K-Means集群的對應關係

Now, compare the cluster centroids to each other either by dividing the data points into groups and then using tapply, or by looking at the output of kmeansClust\(centers, where "kmeansClust" is the name of the output of the kmeans function. (Note that the output of kmeansClust\)centers will be for the normalized data. If you want to look at the average values for the unnormalized data, you need to use tapply like we did for hierarchical clustering.)

table(hierGroups,KMC$cluster)
          
hierGroups    1    2    3    4    5
         1    4    0   98  673    1
         2   92  137  105   92   93
         3  300    4  132   58    0
         4   12    0  653   30  173
         5    0    0    5  329 1008
sapply(split(airlines[,1:7],airlines$hierGroups),colMeans)
                         1          2          3          4          5
Balance         57866.9046 110669.266 198191.575 52335.9136 36255.9098
QualMiles           0.6443   1065.983     30.346     4.8479     2.5112
BonusMiles      10360.1237  22881.763  55795.860 20788.7661  2264.7876
BonusTrans         10.8235     18.229     19.664    17.0876     2.9732
FlightMiles        83.1843   2613.418    327.676   111.5737   119.3219
FlightTrans         0.3028      7.403      1.069     0.3445     0.4389
DaysSinceEnroll  6235.3647   4402.414   5615.709  2840.8226  3060.0812

Do you expect Cluster 1 of the K-Means clustering output to necessarily be similar to Cluster 1 of the Hierarchical clustering output?

  • No, because cluster ordering is not meaningful in either k-means clustering or hierarchical clustering.


【討論問題】

請你們為這五個族群各起一個名稱

  • 1、一般(精緻)會員
    • 註冊已久卻顯少使用的會員,可能對本公司有高度的忠誠度,或受誘於加入會員時的獎勵,然使用需求卻不高
  • 2、尊榮黃金(鑽石)會員
    • 購買公司主要服務的VIP顧客,人數雖最小,卻可能為公司主要的獲利來源
  • 3、便車老司機
    • 吸收了公司提供額外的福利卻幾乎沒有消費主要服務的客群,不只是搭便車,幾乎是駕駛便車的人
  • 4、青少年會員
    • 加入會員時間甚短,如青少年般心性未定的會員,可能成長為尊榮黃金會員,但也有可能成為便車老司機。
  • 5、潛水會員
    • 加入會員時間尚短,目前仍潛在水底,未有明確消費行為潛出水面的會員

請你們為這五個族群各設計一個行銷策略

搭配app提供客製化服務

  • 一般(精緻)會員
    • 目標:持續品牌喚起,維持與顧客的關係
    • 定時發送電子coupon,提醒顧客優惠訊息,一方面確保顧客在有需求時首先聯想起本公司,另一方面電子coupon沒有menu cost,若顧客未使用而超過有效期限,公司不會有太大損失。
  • 尊榮黃金(鑽石)會員
    • 目標:維持服務流暢性,提供消費特權
    • 航空業因轉換成本高,顧客常有消費者隋性,因此針對此客群的行銷方案並非另提出促銷優惠,而是確保其服務流程的流暢並提出一系列消費特權配套方案。如:僅對其提供24小時保證20分鐘內回覆訊息的線上客服,每架班機保留數個VIP座位,以及生日當月招待會貝(同行一人)出國旅遊來回機票等。
  • 便車老司機
    • 目標:削減其享用的bonus以降低公司機會成本
    • 針對其提供組合配套方案,以部分點數搭配較低價格,激發其消費動機。
  • 青少年會員
    • 目標:避免其成為便車老司機
    • 調整Bonus方案,避免其太易於累積BonusMiles及BonusTrans,並不定期提出航班促銷的“快閃低價”,助其積累FlightMiles及FlightTrans同時培養品牌忠誠及喜好。
  • 潛水會員
    • 除了定時宣導公司訊息之外,暫時不做行銷策略

統計上最好的分群也是實務上最好的分群嗎?

+ 不一定,還是要看分析者本身的判斷,根據他的專業知識來挑選合理、可解釋(說出故事)的分群狀況。

除了考慮群間和群間距離之外,實務上的分群通常還需要考慮那些因數?

  • 資料分布密度
  • 資料分布形狀
  • 人口資料:如性別、年齡、收入、職業等,甚至婚姻狀態及家庭成員人數等。
  • 其他消費習慣:如近3年、1年、半年的消費次數、“金額”,以及常去的國家等。






LS0tDQp0aXRsZTogIkFTNi0yIEdyb3VwNCDoiKrnqbrlhazlj7jnmoTluILloLTljYDpmpQiDQphdXRob3I6ICLnjovmrKMsIE0wNjQxMTEwMzkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8YnI+DQoNCioq5Li76KaB6K2w6aGM77ya5L6d6aGn5a6i5bGs5oCn5YGa5biC5aC05Y2A6ZqUKioNCg0KKirlrbjnv5Lph43pu57vvJoqKg0KDQorIOWIqeeUqOmbhue+pOWIhuaekOWBmuW4guWgtOWNgOmalA0KKyDos4fmlpnluLjmhYvljJYNCisg6LOH5paZ6KaW6Ka65YyWDQorIOaXj+e+pOeJueaAp+iIh+ihjOmKt+etlueVpQ0KKyDooYzpirflt6Xlhbd2c+ihjOmKt+WwjeixoQ0KDQoNCmBgYHtyIGVjaG89VCwgbWVzc2FnZT1GLCBjYWNoZT1GLCB3YXJuaW5nPUZ9DQpybShsaXN0PWxzKGFsbD1UKSkNClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsIkMiKQ0Kb3B0aW9ucyhkaWdpdHM9NCwgc2NpcGVuPTEyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoY2FyZXQpDQpgYGANCjxicj4NCg0KLSAtIC0NCg0KIyMjIDEuIOizh+aWmeW4uOaFi+WMlg0KDQojIyMjIyAxLjEg6LOH5paZ5pGY6KaBDQpSZWFkIHRoZSBkYXRhc2V0IEFpcmxpbmVzQ2x1c3Rlci5jc3YgaW50byBSIGFuZCBjYWxsIGl0ICJhaXJsaW5lcyIuIExvb2tpbmcgYXQgdGhlIHN1bW1hcnkgb2YgYWlybGluZXMsIA0KYGBge3J9DQpnZXR3ZCgpDQphaXJsaW5lcyA9IHJlYWQuY3N2KCJkYXRhL0FpcmxpbmVzQ2x1c3Rlci5jc3YiKQ0Kc3VtbWFyeShhaXJsaW5lcykNCmBgYA0KDQpfd2hpY2ggVFdPIHZhcmlhYmxlcyBoYXZlIChvbiBhdmVyYWdlKSB0aGUgc21hbGxlc3QgdmFsdWVzP18NCg0KKyBCb251c1RyYW5zDQorIEZsaWdodFRyYW5zDQoNCl9XaGljaCBUV08gdmFyaWFibGVzIGhhdmUgKG9uIGF2ZXJhZ2UpIHRoZSBsYXJnZXN0IHZhbHVlcz9fDQoNCisgQmFsYW5jZSANCisgQm9udXNNaWxlcw0KDQoNCiMjIyMjIDEuMiDngrrnlJrpurzopoHlgZros4fmlpnluLjmhYvljJYNCkluIHRoaXMgcHJvYmxlbSwgd2Ugd2lsbCBub3JtYWxpemUgb3VyIGRhdGEgYmVmb3JlIHdlIHJ1biB0aGUgY2x1c3RlcmluZyBhbGdvcml0aG1zLiANCg0KX1doeSBpcyBpdCBpbXBvcnRhbnQgdG8gbm9ybWFsaXplIHRoZSBkYXRhIGJlZm9yZSBjbHVzdGVyaW5nP18NCg0KKyBJZiB3ZSBkb24ndCBub3JtYWxpemUgdGhlIGRhdGEsIHRoZSBjbHVzdGVyaW5nIHdpbGwgYmUgZG9taW5hdGVkIGJ5IHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgb24gYSBsYXJnZXIgc2NhbGUuDQoNCiMjIyMjIDEuMyDkvb/nlKhgY2FyZXRg5aWX5Lu25YGa6LOH5paZ5bi45oWL5YyWDQpMZXQncyBnbyBhaGVhZCBhbmQgbm9ybWFsaXplIG91ciBkYXRhLiBZb3UgY2FuIG5vcm1hbGl6ZSB0aGUgdmFyaWFibGVzIGluIGEgZGF0YSBmcmFtZSBieSB1c2luZyB0aGUgcHJlUHJvY2VzcyBmdW5jdGlvbiBpbiB0aGUgImNhcmV0IiBwYWNrYWdlLiBZb3Ugc2hvdWxkIGFscmVhZHkgaGF2ZSB0aGlzIHBhY2thZ2UgaW5zdGFsbGVkIGZyb20gV2VlayA0LCBidXQgaWYgbm90LCBnbyBhaGVhZCBhbmQgaW5zdGFsbCBpdCB3aXRoIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikuIFRoZW4gbG9hZCB0aGUgcGFja2FnZSB3aXRoIGxpYnJhcnkoY2FyZXQpLg0KDQpOb3csIGNyZWF0ZSBhIG5vcm1hbGl6ZWQgZGF0YSBmcmFtZSBjYWxsZWQgImFpcmxpbmVzTm9ybSIgYnkgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzOg0KDQpwcmVwcm9jID0gcHJlUHJvY2VzcyhhaXJsaW5lcykNCg0KYWlybGluZXNOb3JtID0gcHJlZGljdChwcmVwcm9jLCBhaXJsaW5lcykNCg0KVGhlIGZpcnN0IGNvbW1hbmQgcHJlLXByb2Nlc3NlcyB0aGUgZGF0YSwgYW5kIHRoZSBzZWNvbmQgY29tbWFuZCBwZXJmb3JtcyB0aGUgbm9ybWFsaXphdGlvbi4gSWYgeW91IGxvb2sgYXQgdGhlIHN1bW1hcnkgb2YgYWlybGluZXNOb3JtLCB5b3Ugc2hvdWxkIHNlZSB0aGF0IGFsbCBvZiB0aGUgdmFyaWFibGVzIG5vdyBoYXZlIG1lYW4gemVyby4gWW91IGNhbiBhbHNvIHNlZSB0aGF0IGVhY2ggb2YgdGhlIHZhcmlhYmxlcyBoYXMgc3RhbmRhcmQgZGV2aWF0aW9uIDEgYnkgdXNpbmcgdGhlIHNkKCkgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KbGlicmFyeShjYXJldCkNCnByZXByb2MgPSBwcmVQcm9jZXNzKGFpcmxpbmVzKQ0KYWlybGluZXNOb3JtID0gcHJlZGljdChwcmVwcm9jLCBhaXJsaW5lcykNCnN1bW1hcnkoYWlybGluZXNOb3JtKQ0KDQpgYGANCg0KDQpJbiB0aGUgbm9ybWFsaXplZCBkYXRhLCBfd2hpY2ggdmFyaWFibGUgaGFzIHRoZSBsYXJnZXN0IG1heGltdW0gdmFsdWU/Xw0KDQorIEZsaWdodE1pbGVzDQoNCkluIHRoZSBub3JtYWxpemVkIGRhdGEsIF93aGljaCB2YXJpYWJsZSBoYXMgdGhlIHNtYWxsZXN0IG1pbmltdW0gdmFsdWU/Xw0KDQorIERheXNTaW5jZUVucm9sbCANCg0KPGJyPg0KDQotIC0gLQ0KDQojIyMgMi4g5bGk57Sa5byP6ZuG576k5YiG5p6QDQoNCiMjIyMjIDIuMSDkvp3mk5rmqLnni4DlnJblkozmh4nnlKjpnIDmsYLmsbrlrprnvqTmlbgNCkNvbXB1dGUgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIGRhdGEgcG9pbnRzICh1c2luZyBldWNsaWRlYW4gZGlzdGFuY2UpIGFuZCB0aGVuIHJ1biB0aGUgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgYWxnb3JpdGhtICh1c2luZyBtZXRob2Q9IndhcmQuRCIpIG9uIHRoZSBub3JtYWxpemVkIGRhdGEuIEl0IG1heSB0YWtlIGEgZmV3IG1pbnV0ZXMgZm9yIHRoZSBjb21tYW5kcyB0byBmaW5pc2ggc2luY2UgdGhlIGRhdGFzZXQgaGFzIGEgbGFyZ2UgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuDQoNClRoZW4sIHBsb3QgdGhlIGRlbmRyb2dyYW0gb2YgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHByb2Nlc3MuIFN1cHBvc2UgdGhlIGFpcmxpbmUgaXMgbG9va2luZyBmb3Igc29tZXdoZXJlIGJldHdlZW4gMiBhbmQgMTAgY2x1c3RlcnMuIA0KYGBge3J9DQpkaXN0YW5jZSA9IGRpc3QoYWlybGluZXNOb3JtLG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KY2x1c3RlcmFpcmxpbmVzID0gaGNsdXN0KGRpc3RhbmNlLG1ldGhvZD0id2FyZC5EIikNCnBsb3QoY2x1c3RlcmFpcmxpbmVzKQ0KYGBgDQpBY2NvcmRpbmcgdG8gdGhlIGRlbmRyb2dyYW0sIF93aGljaCBvZiB0aGUgZm9sbG93aW5nIGlzIE5PVCBhIGdvb2QgY2hvaWNlIGZvciB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzP18NCg0KKyA2DQoNCiMjIyMjIDIuMiDliIblibLnvqTntYQNClN1cHBvc2UgdGhhdCBhZnRlciBsb29raW5nIGF0IHRoZSBkZW5kcm9ncmFtIGFuZCBkaXNjdXNzaW5nIHdpdGggdGhlIG1hcmtldGluZyBkZXBhcnRtZW50LCB0aGUgYWlybGluZSBkZWNpZGVzIHRvIHByb2NlZWQgd2l0aCA1IGNsdXN0ZXJzLiBEaXZpZGUgdGhlIGRhdGEgcG9pbnRzIGludG8gNSBjbHVzdGVycyBieSB1c2luZyB0aGUgY3V0cmVlIGZ1bmN0aW9uLiANCmBgYHtyfQ0KaGllckdyb3VwcyA9IGN1dHJlZShjbHVzdGVyYWlybGluZXMsIGsgPSA1KQ0KdGFibGUoaGllckdyb3VwcykNCmBgYA0KX0hvdyBtYW55IGRhdGEgcG9pbnRzIGFyZSBpbiBDbHVzdGVyIDE/Xw0KDQorIDc3Ng0KDQojIyMjIyAyLjMg5b6e5Y2A6ZqU6K6K5pW455qE5bmz5Z2H5YC85o6o6KuW5peP576k54m55oCnDQpOb3csIHVzZSB0YXBwbHkgdG8gY29tcGFyZSB0aGUgYXZlcmFnZSB2YWx1ZXMgaW4gZWFjaCBvZiB0aGUgdmFyaWFibGVzIGZvciB0aGUgNSBjbHVzdGVycyAodGhlIGNlbnRyb2lkcyBvZiB0aGUgY2x1c3RlcnMpLiBZb3UgbWF5IHdhbnQgdG8gY29tcHV0ZSB0aGUgYXZlcmFnZSB2YWx1ZXMgb2YgdGhlIHVubm9ybWFsaXplZCBkYXRhIHNvIHRoYXQgaXQgaXMgZWFzaWVyIHRvIGludGVycHJldC4gWW91IGNhbiBkbyB0aGlzIGZvciB0aGUgdmFyaWFibGUgIkJhbGFuY2UiIHdpdGggdGhlIGZvbGxvd2luZyBjb21tYW5kOg0KDQp0YXBwbHkoYWlybGluZXMkQmFsYW5jZSwgY2x1c3Rlckdyb3VwcywgbWVhbikNCmBgYHtyfQ0KbmFtZXMoYWlybGluZXMpDQphaXJsaW5lcyRoaWVyR3JvdXBzID0gaGllckdyb3Vwcw0Kc2FwcGx5KHNwbGl0KGFpcmxpbmVzWywxOjddLCBoaWVyR3JvdXBzKSwgY29sTWVhbnMpDQoNCmBgYA0KQ29tcGFyZWQgdG8gdGhlIG90aGVyIGNsdXN0ZXJzLCBfQ2x1c3RlciAxIGhhcyB0aGUgbGFyZ2VzdCBhdmVyYWdlIHZhbHVlcyBpbiB3aGljaCB2YXJpYWJsZXMgKGlmIGFueSk/IFNlbGVjdCBhbGwgdGhhdCBhcHBseS5fDQoNCisgRGF5c1NpbmNlRW5yb2xsIA0KDQpfSG93IHdvdWxkIHlvdSBkZXNjcmliZSB0aGUgY3VzdG9tZXJzIGluIENsdXN0ZXIgMT9fDQoNCisgSW5mcmVxdWVudCBidXQgbG95YWwgY3VzdG9tZXJzLg0KDQojIyMjIyAyLjQgQ2x1c3RlciAyDQoNCkNvbXBhcmVkIHRvIHRoZSBvdGhlciBjbHVzdGVycywgX0NsdXN0ZXIgMiBoYXMgdGhlIGxhcmdlc3QgYXZlcmFnZSB2YWx1ZXMgaW4gd2hpY2ggdmFyaWFibGVzIChpZiBhbnkpPyBTZWxlY3QgYWxsIHRoYXQgYXBwbHkuXw0KDQorIFF1YWxNaWxlcw0KKyBGbGlnaHRNaWxlcw0KKyBGbGlnaHRUcmFucw0KDQpfSG93IHdvdWxkIHlvdSBkZXNjcmliZSB0aGUgY3VzdG9tZXJzIGluIENsdXN0ZXIgMj9fDQoNCisgQ3VzdG9tZXJzIHdobyBoYXZlIGFjY3VtdWxhdGVkIGEgbGFyZ2UgYW1vdW50IG9mIG1pbGVzLCBhbmQgdGhlIG9uZXMgd2l0aCB0aGUgbGFyZ2VzdCBudW1iZXIgb2YgZmxpZ2h0IHRyYW5zYWN0aW9ucy4NCg0KIyMjIyMgMi41IENsdXN0ZXIgMw0KQ29tcGFyZWQgdG8gdGhlIG90aGVyIGNsdXN0ZXJzLCBfQ2x1c3RlciAzIGhhcyB0aGUgbGFyZ2VzdCBhdmVyYWdlIHZhbHVlcyBpbiB3aGljaCB2YXJpYWJsZXMgKGlmIGFueSk/IFNlbGVjdCBhbGwgdGhhdCBhcHBseS5fDQoNCisgQmFsYW5jZQ0KKyBCb251c01pbGVzDQorIEJvbnVzVHJhbnMNCg0KX0hvdyB3b3VsZCB5b3UgZGVzY3JpYmUgdGhlIGN1c3RvbWVycyBpbiBDbHVzdGVyIDM/Xw0KDQorIEN1c3RvbWVycyB3aG8gaGF2ZSBhY2N1bXVsYXRlZCBhIGxhcmdlIGFtb3VudCBvZiBtaWxlcywgbW9zdGx5IHRocm91Z2ggbm9uLWZsaWdodCB0cmFuc2FjdGlvbnMuIOato+ehrg0KDQojIyMjIyAyLjYgQ2x1c3RlciA0DQpDb21wYXJlZCB0byB0aGUgb3RoZXIgY2x1c3RlcnMsIF9DbHVzdGVyIDQgaGFzIHRoZSBsYXJnZXN0IGF2ZXJhZ2UgdmFsdWVzIGluIHdoaWNoIHZhcmlhYmxlcyAoaWYgYW55KT8gU2VsZWN0IGFsbCB0aGF0IGFwcGx5Ll8NCg0KKyBOb25lDQoNCl9Ib3cgd291bGQgeW91IGRlc2NyaWJlIHRoZSBjdXN0b21lcnMgaW4gQ2x1c3RlciA0P18NCg0KKyBSZWxhdGl2ZWx5IG5ldyBjdXN0b21lcnMgd2hvIHNlZW0gdG8gYmUgYWNjdW11bGF0aW5nIG1pbGVzLCBtb3N0bHkgdGhyb3VnaCBub24tZmxpZ2h0IHRyYW5zYWN0aW9ucy4gDQoNCiMjIyMjIDIuNyBDbHVzdGVyIDUNCkNvbXBhcmVkIHRvIHRoZSBvdGhlciBjbHVzdGVycywgX0NsdXN0ZXIgNSBoYXMgdGhlIGxhcmdlc3QgYXZlcmFnZSB2YWx1ZXMgaW4gd2hpY2ggdmFyaWFibGVzIChpZiBhbnkpPyBTZWxlY3QgYWxsIHRoYXQgYXBwbHkuXw0KDQorIE5vbmUgDQoNCl9Ib3cgd291bGQgeW91IGRlc2NyaWJlIHRoZSBjdXN0b21lcnMgaW4gQ2x1c3RlciA1P18NCg0KKyBSZWxhdGl2ZWx5IG5ldyBjdXN0b21lcnMgd2hvIGRvbid0IHVzZSB0aGUgYWlybGluZSB2ZXJ5IG9mdGVuLg0KDQotIC0gLQ0KDQojIyMgMy4gSy1NZWFuc+mbhue+pOWIhuaekA0KDQojIyMjIyAzLjEgSy1NZWFuc+mbhue+pOWIhuaekA0KTm93IHJ1biB0aGUgay1tZWFucyBjbHVzdGVyaW5nIGFsZ29yaXRobSBvbiB0aGUgbm9ybWFsaXplZCBkYXRhLCBhZ2FpbiBjcmVhdGluZyA1IGNsdXN0ZXJzLiBTZXQgdGhlIHNlZWQgdG8gODggcmlnaHQgYmVmb3JlIHJ1bm5pbmcgdGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtLCBhbmQgc2V0IHRoZSBhcmd1bWVudCBpdGVyLm1heCB0byAxMDAwLg0KDQpgYGB7cn0NCnNldC5zZWVkKDg4KQ0KS01DID0ga21lYW5zKGFpcmxpbmVzTm9ybSwgY2VudGVycyA9IDUsaXRlci5tYXggPTEwMDAgKQ0KdGFibGUoS01DJGNsdXN0ZXIpDQpgYGANCl9Ib3cgbWFueSBjbHVzdGVycyBoYXZlIG1vcmUgdGhhbiAxLDAwMCBvYnNlcnZhdGlvbnM/Xw0KDQorIDINCg0KIyMjIyMgMy4yIEhpZXJhcmNoaWNhbOWSjEstTWVhbnPpm4bnvqTnmoTlsI3mh4npl5zkv4INCk5vdywgY29tcGFyZSB0aGUgY2x1c3RlciBjZW50cm9pZHMgdG8gZWFjaCBvdGhlciBlaXRoZXIgYnkgZGl2aWRpbmcgdGhlIGRhdGEgcG9pbnRzIGludG8gZ3JvdXBzIGFuZCB0aGVuIHVzaW5nIHRhcHBseSwgb3IgYnkgbG9va2luZyBhdCB0aGUgb3V0cHV0IG9mIGttZWFuc0NsdXN0JGNlbnRlcnMsIHdoZXJlICJrbWVhbnNDbHVzdCIgaXMgdGhlIG5hbWUgb2YgdGhlIG91dHB1dCBvZiB0aGUga21lYW5zIGZ1bmN0aW9uLiAoTm90ZSB0aGF0IHRoZSBvdXRwdXQgb2Yga21lYW5zQ2x1c3QkY2VudGVycyB3aWxsIGJlIGZvciB0aGUgbm9ybWFsaXplZCBkYXRhLiBJZiB5b3Ugd2FudCB0byBsb29rIGF0IHRoZSBhdmVyYWdlIHZhbHVlcyBmb3IgdGhlIHVubm9ybWFsaXplZCBkYXRhLCB5b3UgbmVlZCB0byB1c2UgdGFwcGx5IGxpa2Ugd2UgZGlkIGZvciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4pDQoNCmBgYHtyfQ0KdGFibGUoaGllckdyb3VwcyxLTUMkY2x1c3RlcikNCg0Kc2FwcGx5KHNwbGl0KGFpcmxpbmVzWywxOjddLGFpcmxpbmVzJGhpZXJHcm91cHMpLGNvbE1lYW5zKQ0KDQoNCmBgYA0KDQpfRG8geW91IGV4cGVjdCBDbHVzdGVyIDEgb2YgdGhlIEstTWVhbnMgY2x1c3RlcmluZyBvdXRwdXQgdG8gbmVjZXNzYXJpbHkgYmUgc2ltaWxhciB0byBDbHVzdGVyIDEgb2YgdGhlIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG91dHB1dD9fDQoNCisgTm8sIGJlY2F1c2UgY2x1c3RlciBvcmRlcmluZyBpcyBub3QgbWVhbmluZ2Z1bCBpbiBlaXRoZXIgay1tZWFucyBjbHVzdGVyaW5nIG9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLg0KDQoNCjxicj4NCg0KIyMjIyMg44CQ6KiO6KuW5ZWP6aGM44CRDQoNCuiri+S9oOWAkeeCuumAmeS6lOWAi+aXj+e+pOWQhOi1t+S4gOWAi+WQjeeosQ0KDQorIDHjgIHkuIDoiKwo57K+57e7Keacg+WToQ0KICAgICsg6Ki75YaK5bey5LmF5Y276aGv5bCR5L2/55So55qE5pyD5ZOh77yM5Y+v6IO95bCN5pys5YWs5Y+45pyJ6auY5bqm55qE5b+g6Kqg5bqm77yM5oiW5Y+X6KqY5pa85Yqg5YWl5pyD5ZOh5pmC55qE542O5Yu177yM54S25L2/55So6ZyA5rGC5Y275LiN6auYDQogDQorIDLjgIHlsIrmpq7pu4Pph5Eo6ZG955+zKeacg+WToQ0KICAgICsg6LO86LK35YWs5Y+45Li76KaB5pyN5YuZ55qEVklQ6aGn5a6i77yM5Lq65pW46ZuW5pyA5bCP77yM5Y275Y+v6IO954K65YWs5Y+45Li76KaB55qE542y5Yip5L6G5rqQDQoNCisgM+OAgeS+v+i7iuiAgeWPuOapnw0KICAgICsg5ZC45pS25LqG5YWs5Y+45o+Q5L6b6aGN5aSW55qE56aP5Yip5Y275bm+5LmO5rKS5pyJ5raI6LK75Li76KaB5pyN5YuZ55qE5a6i576k77yM5LiN5Y+q5piv5pCt5L6/6LuK77yM5bm+5LmO5piv6aeV6aeb5L6/6LuK55qE5Lq6DQoNCisgNOOAgemdkuWwkeW5tOacg+WToQ0KICAgICsg5Yqg5YWl5pyD5ZOh5pmC6ZaT55Sa55+t77yM5aaC6Z2S5bCR5bm06Iis5b+D5oCn5pyq5a6a55qE5pyD5ZOh77yM5Y+v6IO95oiQ6ZW354K65bCK5qau6buD6YeR5pyD5ZOh77yM5L2G5Lmf5pyJ5Y+v6IO95oiQ54K65L6/6LuK6ICB5Y+45qmf44CCDQoNCisgNeOAgea9m+awtOacg+WToQ0KICAgICsg5Yqg5YWl5pyD5ZOh5pmC6ZaT5bCa55+t77yM55uu5YmN5LuN5r2b5Zyo5rC05bqV77yM5pyq5pyJ5piO56K65raI6LK76KGM54K65r2b5Ye65rC06Z2i55qE5pyD5ZOhDQoNCuiri+S9oOWAkeeCuumAmeS6lOWAi+aXj+e+pOWQhOioreioiOS4gOWAi+ihjOmKt+etlueVpQ0KDQrmkK3phY1hcHDmj5DkvpvlrqLoo73ljJbmnI3li5kNCg0KKyDkuIDoiKwo57K+57e7Keacg+WToQ0KICAgICsg55uu5qiZ77ya5oyB57qM5ZOB54mM5Zaa6LW377yM57at5oyB6IiH6aGn5a6i55qE6Zec5L+CDQogICAgKyDlrprmmYLnmbzpgIHpm7vlrZBjb3Vwb27vvIzmj5DphpLpoaflrqLlhKrmg6DoqIrmga/vvIzkuIDmlrnpnaLnorrkv53poaflrqLlnKjmnInpnIDmsYLmmYLpppblhYjoga/mg7PotbfmnKzlhazlj7jvvIzlj6bkuIDmlrnpnaLpm7vlrZBjb3Vwb27mspLmnIltZW51IGNvc3TvvIzoi6XpoaflrqLmnKrkvb/nlKjogIzotoXpgY7mnInmlYjmnJ/pmZDvvIzlhazlj7jkuI3mnIPmnInlpKrlpKfmkI3lpLHjgIINCiANCisg5bCK5qau6buD6YeRKOmRveefsynmnIPlk6ENCiAgICArIOebruaome+8mue2reaMgeacjeWLmea1geaaouaAp++8jOaPkOS+m+a2iOiyu+eJueasig0KICAgICsg6Iiq56m65qWt5Zug6L2J5o+b5oiQ5pys6auY77yM6aGn5a6i5bi45pyJ5raI6LK76ICF6ZqL5oCn77yM5Zug5q2k6Yed5bCN5q2k5a6i576k55qE6KGM6Yq35pa55qGI5Lim6Z2e5Y+m5o+Q5Ye65L+D6Yq35YSq5oOg77yM6ICM5piv56K65L+d5YW25pyN5YuZ5rWB56iL55qE5rWB5pqi5Lim5o+Q5Ye65LiA57O75YiX5raI6LK754m55qyK6YWN5aWX5pa55qGI44CC5aaC77ya5YOF5bCN5YW25o+Q5L6bMjTlsI/mmYLkv53orYkyMOWIhumQmOWFp+WbnuimhuioiuaBr+eahOe3muS4iuWuouacje+8jOavj+aetuePreapn+S/neeVmeaVuOWAi1ZJUOW6p+S9je+8jOS7peWPiueUn+aXpeeVtuaciOaLm+W+heacg+iynSjlkIzooYzkuIDkurop5Ye65ZyL5peF6YGK5L6G5Zue5qmf56Wo562J44CCDQoNCg0KKyDkvr/ou4rogIHlj7jmqZ8NCiAgICArIOebruaome+8muWJiua4m+WFtuS6q+eUqOeahGJvbnVz5Lul6ZmN5L2O5YWs5Y+45qmf5pyD5oiQ5pysDQogICAgKyDph53lsI3lhbbmj5DkvpvntYTlkIjphY3lpZfmlrnmoYjvvIzku6Xpg6jliIbpu57mlbjmkK3phY3ovIPkvY7lg7nmoLzvvIzmv4Dnmbzlhbbmtojosrvli5XmqZ/jgIINCiANCg0KKyDpnZLlsJHlubTmnIPlk6ENCiAgICArIOebruaome+8mumBv+WFjeWFtuaIkOeCuuS+v+i7iuiAgeWPuOapnw0KICAgICsg6Kq/5pW0Qm9udXPmlrnmoYjvvIzpgb/lhY3lhbblpKrmmJPmlrzntK/nqY1Cb251c01pbGVz5Y+KQm9udXNUcmFuc++8jOS4puS4jeWumuacn+aPkOWHuuiIquePreS/g+mKt+eahCLlv6vploPkvY7lg7ki77yM5Yqp5YW256mN57SvRmxpZ2h0TWlsZXPlj4pGbGlnaHRUcmFuc+WQjOaZguWfuemkiuWTgeeJjOW/oOiqoOWPiuWWnOWlveOAgg0KDQorIOa9m+awtOacg+WToQ0KICAgICsg6Zmk5LqG5a6a5pmC5a6j5bCO5YWs5Y+46KiK5oGv5LmL5aSW77yM5pqr5pmC5LiN5YGa6KGM6Yq3562W55WlDQoNCue1seioiOS4iuacgOWlveeahOWIhue+pOS5n+aYr+WvpuWLmeS4iuacgOWlveeahOWIhue+pOWXju+8nyANCg0KICAgICsg5LiN5LiA5a6a77yM6YKE5piv6KaB55yL5YiG5p6Q6ICF5pys6Lqr55qE5Yik5pa377yM5qC55pOa5LuW55qE5bCI5qWt55+l6K2Y5L6G5oyR6YG45ZCI55CG44CB5Y+v6Kej6YeLKOiqquWHuuaVheS6iynnmoTliIbnvqTni4Dms4HjgIINCg0K6Zmk5LqG6ICD5oWu576k6ZaT5ZKM576k6ZaT6Led6Zui5LmL5aSW77yM5a+m5YuZ5LiK55qE5YiG576k6YCa5bi46YKE6ZyA6KaB6ICD5oWu6YKj5Lqb5Zug5pW477yfIA0KDQorIOizh+aWmeWIhuW4g+WvhuW6pg0KKyDos4fmlpnliIbluIPlvaLni4ANCisg5Lq65Y+j6LOH5paZ77ya5aaC5oCn5Yil44CB5bm06b2h44CB5pS25YWl44CB6IG35qWt562J77yM55Sa6Iez5ama5ae754uA5oWL5Y+K5a625bqt5oiQ5ZOh5Lq65pW4562J44CCDQorIOWFtuS7lua2iOiyu+e/kuaFo++8muWmgui/kTPlubTjgIEx5bm044CB5Y2K5bm055qE5raI6LK75qyh5pW444CBIumHkemhjSLvvIzku6Xlj4rluLjljrvnmoTlnIvlrrbnrYnjgIINCg0KDQotIC0gLQ0KDQo8YnI+PGJyPjxicj48YnI+PGJyPg0KDQo8c3R5bGU+DQouY2FwdGlvbiB7DQogIGNvbG9yOiAjNzc3Ow0KICBtYXJnaW4tdG9wOiAxMHB4Ow0KfQ0KcCBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwcmUgew0KICB3b3JkLWJyZWFrOiBub3JtYWw7DQogIHdvcmQtd3JhcDogbm9ybWFsOw0KICBsaW5lLWhlaWdodDogMTsNCn0NCnByZSBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwLGxpIHsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCi5yew0KICBsaW5lLWhlaWdodDogMS4yOw0KfQ0KDQp0aXRsZXsNCiAgY29sb3I6ICNjYzAwMDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpib2R5ew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDEsaDIsaDMsaDQsaDV7DQogIGNvbG9yOiAjMDA4ODAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDN7DQogIGNvbG9yOiAjYjM2YjAwOw0KICBiYWNrZ3JvdW5kOiAjZmZlMGIzOw0KICBsaW5lLWhlaWdodDogMjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmg1ew0KICBjb2xvcjogIzAwNjAwMDsNCiAgYmFja2dyb3VuZDogI2ZmZmZlMDsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQplbXsNCiAgY29sb3I6ICMwMDAwYzA7DQogIGJhY2tncm91bmQ6ICNmMGYwZjA7DQogIH0NCjwvc3R5bGU+DQoNCg==