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?
Which TWO variables have (on average) the largest values?
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?
In the normalized data, which variable has the smallest minimum value?
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?
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?
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.
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.
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.
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?
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==