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

學習重點:

rm(list=ls(all=T))
Sys.setlocale("LC_ALL","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,

A = read.csv('data/AirlinesCluster.csv')
summary(A)
colMeans(A) %>% sort

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

  • BonusTrans and FlightTrans

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

  • Balance and 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 variables that are on a larger scale will contribute much more to the distance calculation, and thus will dominate the clustering.
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(A)
AN = predict(preproc, A)  
summary(AN)
apply(AN, 2, mean) %>% round(3)
apply(AN, 2, sd) %>% round(3)
apply(AN, 2, max) %>% sort

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

  • FlightMiles
apply(AN, 2, min) %>% sort

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.

d = dist(AN,method="euclidean")
hc = hclust(d, method='ward.D')
plot(hc)

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

  • If you run a horizontal line down the dendrogram, you can see that there is a long time that the line crosses 2 clusters, 3 clusters, or 7 clusters. However, it it hard to see the horizontal line cross 6 clusters. This means that 6 clusters is probably not a good choice.
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.

kg = cutree(hc, k=5)
table(kg)

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)

sapply(split(A,kg), colMeans) %>% round(2) 

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

  • The only variable for which Cluster 1 has large values is DaysSinceEnroll.

How would you describe the customers in Cluster 1?

  • Cluster 1 mostly contains customers with few miles, but who have been with the airline the longest.
2.4 Cluster 2
split(AN,kg) %>% sapply(colMeans) %>% round(2)
par(cex=0.8)
split(AN,kg) %>% sapply(colMeans) %>% barplot(beside=T,col=rainbow(7))
legend('topright',legend=colnames(A),fill=rainbow(7))

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

  • Cluster 2 has the largest average values in the variables QualMiles, FlightMiles and FlightTrans. This cluster also has relatively large values in BonusTrans and Balance.

How would you describe the customers in Cluster 2?

  • Cluster 2 contains customers with a large amount of miles, mostly accumulated through 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.

  • Cluster 3 has the largest values in Balance, BonusMiles, and BonusTrans. While it also has relatively large values in other variables, these are the three for which it has the largest values.

How would you describe the customers in Cluster 3?

  • Cluster 3 mostly contains customers with a lot of miles, and who have earned the miles mostly through bonus 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.

  • Cluster 4 does not have the largest values in any of the variables.

How would you describe the customers in Cluster 4?

  • Cluster 4 customers have the smallest value in DaysSinceEnroll, but they are already accumulating a reasonable number of miles.
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.

  • Cluster 5 does not have the largest values in any of the variables.

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)
km = kmeans(AN, 5, iter.max = 1000)
kg2 = km$cluster
table(kg2)

How many clusters have more than 1,000 observations?

+There are two clusters with more than 1000 observations.

par(cex=0.8)
km$centers %>% round(2) %>% t %>% barplot(beside=T,col=rainbow(7))
legend('topright',legend=colnames(A),fill=rainbow(7))
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(Hierarchical=kg, KMeans=kg2)

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.


【討論問題】

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

  • 賠錢貨
  • 商務客戶(金雞母
  • 潛在客戶
  • 沈睡客戶
  • 殭屍

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

  • 公司不需要花費資源在這些客戶上。

  • 商務客戶,公司應當優先將資源投放在他們身上,對他們做到一對一精準營銷,比如提供相應的優惠政策,提高這類客戶的忠誠度和滿意度,盡可能延長這類客戶的高消費水平。

  • 對那些接近但尚未達到首次兌現機票的會員,對他們進行提醒,使他們達到首次兌現標準並加入會員。

  • 航空公司在運營過程中要積極推測這類客戶的異常情況,進行競爭分析。該群客戶既然是會員,卻許久未搭乘,有可能是其他家航空公司有更誘人的行銷策略。因此我們應該觀察其他航空公司有什麼營銷手法,然後採取有針對性的營銷手段,將沈睡客戶喚醒。

  • 公司不需要花費資源在這些客戶上。

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

  • 並不是。統計上選出的變數,最好的分群不一定適用於實務上,也不一定對實務有貢獻。

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

  • 分組數量有時也是必須考慮的因素,因為有時分組最後的結果會讓人難以解釋。 這時可以改變分組數量,也許不同的分法會讓你看到不同的觀點。






LS0tDQp0aXRsZTogIkFTNi0yIOiIquepuuWFrOWPuOeahOW4guWgtOWNgOmalCINCmF1dGhvcjogIkdST1VQMywgMjAxOC8wNy8yOSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxicj4NCg0KKirkuLvopoHorbDpoYzvvJrkvp3poaflrqLlsazmgKflgZrluILloLTljYDpmpQqKg0KDQoqKuWtuOe/kumHjem7nu+8mioqDQoNCisg5Yip55So6ZuG576k5YiG5p6Q5YGa5biC5aC05Y2A6ZqUDQorIOizh+aWmeW4uOaFi+WMlg0KKyDos4fmlpnoppboprrljJYNCisg5peP576k54m55oCn6IiH6KGM6Yq3562W55WlDQorIOihjOmKt+W3peWFt3Zz6KGM6Yq35bCN6LGhDQoNCg0KYGBge3IgZWNobz1ULCBtZXNzYWdlPUYsIGNhY2hlPUYsIHdhcm5pbmc9Rn0NCnJtKGxpc3Q9bHMoYWxsPVQpKQ0KU3lzLnNldGxvY2FsZSgiTENfQUxMIiwiQyIpDQpvcHRpb25zKGRpZ2l0cz00LCBzY2lwZW49MTIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMgMS4g6LOH5paZ5bi45oWL5YyWDQoNCiMjIyMjIDEuMSDos4fmlpnmkZjopoENClJlYWQgdGhlIGRhdGFzZXQgQWlybGluZXNDbHVzdGVyLmNzdiBpbnRvIFIgYW5kIGNhbGwgaXQgImFpcmxpbmVzIi4gTG9va2luZyBhdCB0aGUgc3VtbWFyeSBvZiBhaXJsaW5lcywgDQpgYGB7cn0NCkEgPSByZWFkLmNzdignZGF0YS9BaXJsaW5lc0NsdXN0ZXIuY3N2JykNCnN1bW1hcnkoQSkNCmBgYA0KYGBge3J9DQpjb2xNZWFucyhBKSAlPiUgc29ydA0KYGBgDQoNCl93aGljaCBUV08gdmFyaWFibGVzIGhhdmUgKG9uIGF2ZXJhZ2UpIHRoZSBzbWFsbGVzdCB2YWx1ZXM/Xw0KDQorIEJvbnVzVHJhbnMgYW5kIEZsaWdodFRyYW5zDQoNCl9XaGljaCBUV08gdmFyaWFibGVzIGhhdmUgKG9uIGF2ZXJhZ2UpIHRoZSBsYXJnZXN0IHZhbHVlcz9fDQoNCisgQmFsYW5jZSBhbmQgQm9udXNNaWxlcw0KDQoNCg0KIyMjIyMgMS4yIOeCuueUmum6vOimgeWBmuizh+aWmeW4uOaFi+WMlg0KSW4gdGhpcyBwcm9ibGVtLCB3ZSB3aWxsIG5vcm1hbGl6ZSBvdXIgZGF0YSBiZWZvcmUgd2UgcnVuIHRoZSBjbHVzdGVyaW5nIGFsZ29yaXRobXMuIA0KDQpfV2h5IGlzIGl0IGltcG9ydGFudCB0byBub3JtYWxpemUgdGhlIGRhdGEgYmVmb3JlIGNsdXN0ZXJpbmc/Xw0KDQorIElmIHdlIGRvbid0IG5vcm1hbGl6ZSB0aGUgZGF0YSwgdGhlIHZhcmlhYmxlcyB0aGF0IGFyZSBvbiBhIGxhcmdlciBzY2FsZSB3aWxsIGNvbnRyaWJ1dGUgbXVjaCBtb3JlIHRvIHRoZSBkaXN0YW5jZSBjYWxjdWxhdGlvbiwgYW5kIHRodXMgd2lsbCBkb21pbmF0ZSB0aGUgY2x1c3RlcmluZy4NCg0KDQojIyMjIyAxLjMg5L2/55SoYGNhcmV0YOWll+S7tuWBmuizh+aWmeW4uOaFi+WMlg0KTGV0J3MgZ28gYWhlYWQgYW5kIG5vcm1hbGl6ZSBvdXIgZGF0YS4gWW91IGNhbiBub3JtYWxpemUgdGhlIHZhcmlhYmxlcyBpbiBhIGRhdGEgZnJhbWUgYnkgdXNpbmcgdGhlIHByZVByb2Nlc3MgZnVuY3Rpb24gaW4gdGhlICJjYXJldCIgcGFja2FnZS4gWW91IHNob3VsZCBhbHJlYWR5IGhhdmUgdGhpcyBwYWNrYWdlIGluc3RhbGxlZCBmcm9tIFdlZWsgNCwgYnV0IGlmIG5vdCwgZ28gYWhlYWQgYW5kIGluc3RhbGwgaXQgd2l0aCBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpLiBUaGVuIGxvYWQgdGhlIHBhY2thZ2Ugd2l0aCBsaWJyYXJ5KGNhcmV0KS4NCg0KTm93LCBjcmVhdGUgYSBub3JtYWxpemVkIGRhdGEgZnJhbWUgY2FsbGVkICJhaXJsaW5lc05vcm0iIGJ5IHJ1bm5pbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kczoNCg0KcHJlcHJvYyA9IHByZVByb2Nlc3MoYWlybGluZXMpDQoNCmFpcmxpbmVzTm9ybSA9IHByZWRpY3QocHJlcHJvYywgYWlybGluZXMpDQoNClRoZSBmaXJzdCBjb21tYW5kIHByZS1wcm9jZXNzZXMgdGhlIGRhdGEsIGFuZCB0aGUgc2Vjb25kIGNvbW1hbmQgcGVyZm9ybXMgdGhlIG5vcm1hbGl6YXRpb24uIElmIHlvdSBsb29rIGF0IHRoZSBzdW1tYXJ5IG9mIGFpcmxpbmVzTm9ybSwgeW91IHNob3VsZCBzZWUgdGhhdCBhbGwgb2YgdGhlIHZhcmlhYmxlcyBub3cgaGF2ZSBtZWFuIHplcm8uIFlvdSBjYW4gYWxzbyBzZWUgdGhhdCBlYWNoIG9mIHRoZSB2YXJpYWJsZXMgaGFzIHN0YW5kYXJkIGRldmlhdGlvbiAxIGJ5IHVzaW5nIHRoZSBzZCgpIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQpwcmVwcm9jID0gcHJlUHJvY2VzcyhBKQ0KQU4gPSBwcmVkaWN0KHByZXByb2MsIEEpICANCnN1bW1hcnkoQU4pDQphcHBseShBTiwgMiwgbWVhbikgJT4lIHJvdW5kKDMpDQphcHBseShBTiwgMiwgc2QpICU+JSByb3VuZCgzKQ0KYGBgDQoNCmBgYHtyfQ0KYXBwbHkoQU4sIDIsIG1heCkgJT4lIHNvcnQNCmBgYA0KDQpJbiB0aGUgbm9ybWFsaXplZCBkYXRhLCBfd2hpY2ggdmFyaWFibGUgaGFzIHRoZSBsYXJnZXN0IG1heGltdW0gdmFsdWU/Xw0KDQorIEZsaWdodE1pbGVzDQoNCg0KYGBge3J9DQphcHBseShBTiwgMiwgbWluKSAlPiUgc29ydA0KYGBgDQoNCkluIHRoZSBub3JtYWxpemVkIGRhdGEsIF93aGljaCB2YXJpYWJsZSBoYXMgdGhlIHNtYWxsZXN0IG1pbmltdW0gdmFsdWU/Xw0KDQorICBEYXlzU2luY2VFbnJvbGwNCg0KDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyAyLiDlsaTntJrlvI/pm4bnvqTliIbmnpANCg0KIyMjIyMgMi4xIOS+neaTmuaoueeLgOWcluWSjOaHieeUqOmcgOaxguaxuuWumue+pOaVuA0KQ29tcHV0ZSB0aGUgZGlzdGFuY2VzIGJldHdlZW4gZGF0YSBwb2ludHMgKHVzaW5nIGV1Y2xpZGVhbiBkaXN0YW5jZSkgYW5kIHRoZW4gcnVuIHRoZSBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhbGdvcml0aG0gKHVzaW5nIG1ldGhvZD0id2FyZC5EIikgb24gdGhlIG5vcm1hbGl6ZWQgZGF0YS4gSXQgbWF5IHRha2UgYSBmZXcgbWludXRlcyBmb3IgdGhlIGNvbW1hbmRzIHRvIGZpbmlzaCBzaW5jZSB0aGUgZGF0YXNldCBoYXMgYSBsYXJnZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4NCg0KVGhlbiwgcGxvdCB0aGUgZGVuZHJvZ3JhbSBvZiB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgcHJvY2Vzcy4gU3VwcG9zZSB0aGUgYWlybGluZSBpcyBsb29raW5nIGZvciBzb21ld2hlcmUgYmV0d2VlbiAyIGFuZCAxMCBjbHVzdGVycy4gDQpgYGB7cn0NCmQgPSBkaXN0KEFOLG1ldGhvZD0iZXVjbGlkZWFuIikNCmhjID0gaGNsdXN0KGQsIG1ldGhvZD0nd2FyZC5EJykNCnBsb3QoaGMpDQpgYGANCkFjY29yZGluZyB0byB0aGUgZGVuZHJvZ3JhbSwgX3doaWNoIG9mIHRoZSBmb2xsb3dpbmcgaXMgTk9UIGEgZ29vZCBjaG9pY2UgZm9yIHRoZSBudW1iZXIgb2YgY2x1c3RlcnM/Xw0KDQorIElmIHlvdSBydW4gYSBob3Jpem9udGFsIGxpbmUgZG93biB0aGUgZGVuZHJvZ3JhbSwgeW91IGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBhIGxvbmcgdGltZSB0aGF0IHRoZSBsaW5lIGNyb3NzZXMgMiBjbHVzdGVycywgMyBjbHVzdGVycywgb3IgNyBjbHVzdGVycy4gSG93ZXZlciwgaXQgaXQgaGFyZCB0byBzZWUgdGhlIGhvcml6b250YWwgbGluZSBjcm9zcyA2IGNsdXN0ZXJzLiBUaGlzIG1lYW5zIHRoYXQgNiBjbHVzdGVycyBpcyBwcm9iYWJseSBub3QgYSBnb29kIGNob2ljZS4NCg0KDQojIyMjIyAyLjIg5YiG5Ymy576k57WEDQpTdXBwb3NlIHRoYXQgYWZ0ZXIgbG9va2luZyBhdCB0aGUgZGVuZHJvZ3JhbSBhbmQgZGlzY3Vzc2luZyB3aXRoIHRoZSBtYXJrZXRpbmcgZGVwYXJ0bWVudCwgdGhlIGFpcmxpbmUgZGVjaWRlcyB0byBwcm9jZWVkIHdpdGggNSBjbHVzdGVycy4gRGl2aWRlIHRoZSBkYXRhIHBvaW50cyBpbnRvIDUgY2x1c3RlcnMgYnkgdXNpbmcgdGhlIGN1dHJlZSBmdW5jdGlvbi4gDQpgYGB7cn0NCmtnID0gY3V0cmVlKGhjLCBrPTUpDQp0YWJsZShrZykNCmBgYA0KX0hvdyBtYW55IGRhdGEgcG9pbnRzIGFyZSBpbiBDbHVzdGVyIDE/Xw0KDQorIDc3Ng0KDQojIyMjIyAyLjMg5b6e5Y2A6ZqU6K6K5pW455qE5bmz5Z2H5YC85o6o6KuW5peP576k54m55oCnDQpOb3csIHVzZSB0YXBwbHkgdG8gY29tcGFyZSB0aGUgYXZlcmFnZSB2YWx1ZXMgaW4gZWFjaCBvZiB0aGUgdmFyaWFibGVzIGZvciB0aGUgNSBjbHVzdGVycyAodGhlIGNlbnRyb2lkcyBvZiB0aGUgY2x1c3RlcnMpLiBZb3UgbWF5IHdhbnQgdG8gY29tcHV0ZSB0aGUgYXZlcmFnZSB2YWx1ZXMgb2YgdGhlIHVubm9ybWFsaXplZCBkYXRhIHNvIHRoYXQgaXQgaXMgZWFzaWVyIHRvIGludGVycHJldC4gWW91IGNhbiBkbyB0aGlzIGZvciB0aGUgdmFyaWFibGUgIkJhbGFuY2UiIHdpdGggdGhlIGZvbGxvd2luZyBjb21tYW5kOg0KDQp0YXBwbHkoYWlybGluZXMkQmFsYW5jZSwgY2x1c3Rlckdyb3VwcywgbWVhbikNCmBgYHtyfQ0Kc2FwcGx5KHNwbGl0KEEsa2cpLCBjb2xNZWFucykgJT4lIHJvdW5kKDIpIA0KYGBgDQpDb21wYXJlZCB0byB0aGUgb3RoZXIgY2x1c3RlcnMsIF9DbHVzdGVyIDEgaGFzIHRoZSBsYXJnZXN0IGF2ZXJhZ2UgdmFsdWVzIGluIHdoaWNoIHZhcmlhYmxlcyAoaWYgYW55KT8gU2VsZWN0IGFsbCB0aGF0IGFwcGx5Ll8NCg0KKyBUaGUgb25seSB2YXJpYWJsZSBmb3Igd2hpY2ggQ2x1c3RlciAxIGhhcyBsYXJnZSB2YWx1ZXMgaXMgRGF5c1NpbmNlRW5yb2xsLg0KDQoNCl9Ib3cgd291bGQgeW91IGRlc2NyaWJlIHRoZSBjdXN0b21lcnMgaW4gQ2x1c3RlciAxP18NCg0KKyBDbHVzdGVyIDEgbW9zdGx5IGNvbnRhaW5zIGN1c3RvbWVycyB3aXRoIGZldyBtaWxlcywgYnV0IHdobyBoYXZlIGJlZW4gd2l0aCB0aGUgYWlybGluZSB0aGUgbG9uZ2VzdC4NCg0KDQojIyMjIyAyLjQgQ2x1c3RlciAyDQpgYGB7cn0NCnNwbGl0KEFOLGtnKSAlPiUgc2FwcGx5KGNvbE1lYW5zKSAlPiUgcm91bmQoMikNCmBgYA0KDQpgYGB7cn0NCnBhcihjZXg9MC44KQ0Kc3BsaXQoQU4sa2cpICU+JSBzYXBwbHkoY29sTWVhbnMpICU+JSBiYXJwbG90KGJlc2lkZT1ULGNvbD1yYWluYm93KDcpKQ0KbGVnZW5kKCd0b3ByaWdodCcsbGVnZW5kPWNvbG5hbWVzKEEpLGZpbGw9cmFpbmJvdyg3KSkNCmBgYA0KDQpDb21wYXJlZCB0byB0aGUgb3RoZXIgY2x1c3RlcnMsIF9DbHVzdGVyIDIgaGFzIHRoZSBsYXJnZXN0IGF2ZXJhZ2UgdmFsdWVzIGluIHdoaWNoIHZhcmlhYmxlcyAoaWYgYW55KT8gU2VsZWN0IGFsbCB0aGF0IGFwcGx5Ll8NCg0KKyBDbHVzdGVyIDIgaGFzIHRoZSBsYXJnZXN0IGF2ZXJhZ2UgdmFsdWVzIGluIHRoZSB2YXJpYWJsZXMgUXVhbE1pbGVzLCBGbGlnaHRNaWxlcyBhbmQgRmxpZ2h0VHJhbnMuIFRoaXMgY2x1c3RlciBhbHNvIGhhcyByZWxhdGl2ZWx5IGxhcmdlIHZhbHVlcyBpbiBCb251c1RyYW5zIGFuZCBCYWxhbmNlLg0KDQoNCl9Ib3cgd291bGQgeW91IGRlc2NyaWJlIHRoZSBjdXN0b21lcnMgaW4gQ2x1c3RlciAyP18NCg0KKyBDbHVzdGVyIDIgY29udGFpbnMgY3VzdG9tZXJzIHdpdGggYSBsYXJnZSBhbW91bnQgb2YgbWlsZXMsIG1vc3RseSBhY2N1bXVsYXRlZCB0aHJvdWdoIGZsaWdodCB0cmFuc2FjdGlvbnMuDQoNCiMjIyMjIDIuNSBDbHVzdGVyIDMNCkNvbXBhcmVkIHRvIHRoZSBvdGhlciBjbHVzdGVycywgX0NsdXN0ZXIgMyBoYXMgdGhlIGxhcmdlc3QgYXZlcmFnZSB2YWx1ZXMgaW4gd2hpY2ggdmFyaWFibGVzIChpZiBhbnkpPyBTZWxlY3QgYWxsIHRoYXQgYXBwbHkuXw0KDQorIENsdXN0ZXIgMyBoYXMgdGhlIGxhcmdlc3QgdmFsdWVzIGluIEJhbGFuY2UsIEJvbnVzTWlsZXMsIGFuZCBCb251c1RyYW5zLiBXaGlsZSBpdCBhbHNvIGhhcyByZWxhdGl2ZWx5IGxhcmdlIHZhbHVlcyBpbiBvdGhlciB2YXJpYWJsZXMsIHRoZXNlIGFyZSB0aGUgdGhyZWUgZm9yIHdoaWNoIGl0IGhhcyB0aGUgbGFyZ2VzdCB2YWx1ZXMuDQoNCg0KX0hvdyB3b3VsZCB5b3UgZGVzY3JpYmUgdGhlIGN1c3RvbWVycyBpbiBDbHVzdGVyIDM/Xw0KDQorIENsdXN0ZXIgMyBtb3N0bHkgY29udGFpbnMgY3VzdG9tZXJzIHdpdGggYSBsb3Qgb2YgbWlsZXMsIGFuZCB3aG8gaGF2ZSBlYXJuZWQgdGhlIG1pbGVzIG1vc3RseSB0aHJvdWdoIGJvbnVzIHRyYW5zYWN0aW9ucy4NCg0KDQojIyMjIyAyLjYgQ2x1c3RlciA0DQpDb21wYXJlZCB0byB0aGUgb3RoZXIgY2x1c3RlcnMsIF9DbHVzdGVyIDQgaGFzIHRoZSBsYXJnZXN0IGF2ZXJhZ2UgdmFsdWVzIGluIHdoaWNoIHZhcmlhYmxlcyAoaWYgYW55KT8gU2VsZWN0IGFsbCB0aGF0IGFwcGx5Ll8NCg0KKyBDbHVzdGVyIDQgZG9lcyBub3QgaGF2ZSB0aGUgbGFyZ2VzdCB2YWx1ZXMgaW4gYW55IG9mIHRoZSB2YXJpYWJsZXMuDQoNCl9Ib3cgd291bGQgeW91IGRlc2NyaWJlIHRoZSBjdXN0b21lcnMgaW4gQ2x1c3RlciA0P18NCg0KKyBDbHVzdGVyIDQgY3VzdG9tZXJzIGhhdmUgdGhlIHNtYWxsZXN0IHZhbHVlIGluIERheXNTaW5jZUVucm9sbCwgYnV0IHRoZXkgYXJlIGFscmVhZHkgYWNjdW11bGF0aW5nIGEgcmVhc29uYWJsZSBudW1iZXIgb2YgbWlsZXMuDQoNCg0KIyMjIyMgMi43IENsdXN0ZXIgNQ0KQ29tcGFyZWQgdG8gdGhlIG90aGVyIGNsdXN0ZXJzLCBfQ2x1c3RlciA1IGhhcyB0aGUgbGFyZ2VzdCBhdmVyYWdlIHZhbHVlcyBpbiB3aGljaCB2YXJpYWJsZXMgKGlmIGFueSk/IFNlbGVjdCBhbGwgdGhhdCBhcHBseS5fDQoNCisgQ2x1c3RlciA1IGRvZXMgbm90IGhhdmUgdGhlIGxhcmdlc3QgdmFsdWVzIGluIGFueSBvZiB0aGUgdmFyaWFibGVzLg0KDQpfSG93IHdvdWxkIHlvdSBkZXNjcmliZSB0aGUgY3VzdG9tZXJzIGluIENsdXN0ZXIgNT9fDQoNCisgUmVsYXRpdmVseSBuZXcgY3VzdG9tZXJzIHdobyBkb24ndCB1c2UgdGhlIGFpcmxpbmUgdmVyeSBvZnRlbi4gDQoNCi0gLSAtDQoNCiMjIyAzLiBLLU1lYW5z6ZuG576k5YiG5p6QDQoNCiMjIyMjIDMuMSBLLU1lYW5z6ZuG576k5YiG5p6QDQpOb3cgcnVuIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIG9uIHRoZSBub3JtYWxpemVkIGRhdGEsIGFnYWluIGNyZWF0aW5nIDUgY2x1c3RlcnMuIFNldCB0aGUgc2VlZCB0byA4OCByaWdodCBiZWZvcmUgcnVubmluZyB0aGUgY2x1c3RlcmluZyBhbGdvcml0aG0sIGFuZCBzZXQgdGhlIGFyZ3VtZW50IGl0ZXIubWF4IHRvIDEwMDAuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoODgpDQprbSA9IGttZWFucyhBTiwgNSwgaXRlci5tYXggPSAxMDAwKQ0Ka2cyID0ga20kY2x1c3Rlcg0KdGFibGUoa2cyKQ0KYGBgDQpfSG93IG1hbnkgY2x1c3RlcnMgaGF2ZSBtb3JlIHRoYW4gMSwwMDAgb2JzZXJ2YXRpb25zP18NCg0KK1RoZXJlIGFyZSB0d28gY2x1c3RlcnMgd2l0aCBtb3JlIHRoYW4gMTAwMCBvYnNlcnZhdGlvbnMuDQoNCmBgYHtyfQ0KcGFyKGNleD0wLjgpDQprbSRjZW50ZXJzICU+JSByb3VuZCgyKSAlPiUgdCAlPiUgYmFycGxvdChiZXNpZGU9VCxjb2w9cmFpbmJvdyg3KSkNCmxlZ2VuZCgndG9wcmlnaHQnLGxlZ2VuZD1jb2xuYW1lcyhBKSxmaWxsPXJhaW5ib3coNykpDQpgYGANCg0KIyMjIyMgMy4yIEhpZXJhcmNoaWNhbOWSjEstTWVhbnPpm4bnvqTnmoTlsI3mh4npl5zkv4INCk5vdywgY29tcGFyZSB0aGUgY2x1c3RlciBjZW50cm9pZHMgdG8gZWFjaCBvdGhlciBlaXRoZXIgYnkgZGl2aWRpbmcgdGhlIGRhdGEgcG9pbnRzIGludG8gZ3JvdXBzIGFuZCB0aGVuIHVzaW5nIHRhcHBseSwgb3IgYnkgbG9va2luZyBhdCB0aGUgb3V0cHV0IG9mIGttZWFuc0NsdXN0JGNlbnRlcnMsIHdoZXJlICJrbWVhbnNDbHVzdCIgaXMgdGhlIG5hbWUgb2YgdGhlIG91dHB1dCBvZiB0aGUga21lYW5zIGZ1bmN0aW9uLiAoTm90ZSB0aGF0IHRoZSBvdXRwdXQgb2Yga21lYW5zQ2x1c3QkY2VudGVycyB3aWxsIGJlIGZvciB0aGUgbm9ybWFsaXplZCBkYXRhLiBJZiB5b3Ugd2FudCB0byBsb29rIGF0IHRoZSBhdmVyYWdlIHZhbHVlcyBmb3IgdGhlIHVubm9ybWFsaXplZCBkYXRhLCB5b3UgbmVlZCB0byB1c2UgdGFwcGx5IGxpa2Ugd2UgZGlkIGZvciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4pDQpgYGB7cn0NCnRhYmxlKEhpZXJhcmNoaWNhbD1rZywgS01lYW5zPWtnMikNCmBgYA0KDQpfRG8geW91IGV4cGVjdCBDbHVzdGVyIDEgb2YgdGhlIEstTWVhbnMgY2x1c3RlcmluZyBvdXRwdXQgdG8gbmVjZXNzYXJpbHkgYmUgc2ltaWxhciB0byBDbHVzdGVyIDEgb2YgdGhlIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG91dHB1dD9fDQoNCisgTm8sIGJlY2F1c2UgY2x1c3RlciBvcmRlcmluZyBpcyBub3QgbWVhbmluZ2Z1bCBpbiBlaXRoZXIgay1tZWFucyBjbHVzdGVyaW5nIG9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLg0KDQoNCjxicj4NCg0KIyMjIyMg44CQ6KiO6KuW5ZWP6aGM44CRDQoNCuiri+S9oOWAkeeCuumAmeS6lOWAi+aXj+e+pOWQhOi1t+S4gOWAi+WQjeeosQ0KDQorIOizoOmMouiyqA0KKyDllYbli5nlrqLmiLbvvIjph5Hpm57mr40NCisg5r2b5Zyo5a6i5oi2DQorIOayiOedoeWuouaItg0KKyDmrq3lsY0NCg0K6KuL5L2g5YCR54K66YCZ5LqU5YCL5peP576k5ZCE6Kit6KiI5LiA5YCL6KGM6Yq3562W55WlDQoNCisg5YWs5Y+45LiN6ZyA6KaB6Iqx6LK76LOH5rqQ5Zyo6YCZ5Lqb5a6i5oi25LiK44CCDQoNCisg5ZWG5YuZ5a6i5oi277yM5YWs5Y+45oeJ55W25YSq5YWI5bCH6LOH5rqQ5oqV5pS+5Zyo5LuW5YCR6Lqr5LiK77yM5bCN5LuW5YCR5YGa5Yiw5LiA5bCN5LiA57K+5rqW54ef6Yq377yM5q+U5aaC5o+Q5L6b55u45oeJ55qE5YSq5oOg5pS/562W77yM5o+Q6auY6YCZ6aGe5a6i5oi255qE5b+g6Kqg5bqm5ZKM5ru/5oSP5bqm77yM55uh5Y+v6IO95bu26ZW36YCZ6aGe5a6i5oi255qE6auY5raI6LK75rC05bmz44CCDQoNCisg5bCN6YKj5Lqb5o6l6L+R5L2G5bCa5pyq6YGU5Yiw6aaW5qyh5YWM54++5qmf56Wo55qE5pyD5ZOh77yM5bCN5LuW5YCR6YCy6KGM5o+Q6YaS77yM5L2/5LuW5YCR6YGU5Yiw6aaW5qyh5YWM54++5qiZ5rqW5Lim5Yqg5YWl5pyD5ZOh44CCDQoNCisg6Iiq56m65YWs5Y+45Zyo6YGL54ef6YGO56iL5Lit6KaB56mN5qW15o6o5ris6YCZ6aGe5a6i5oi255qE55Ww5bi45oOF5rOB77yM6YCy6KGM56u254it5YiG5p6Q44CC6Kmy576k5a6i5oi25pei54S25piv5pyD5ZOh77yM5Y276Kix5LmF5pyq5pCt5LmY77yM5pyJ5Y+v6IO95piv5YW25LuW5a626Iiq56m65YWs5Y+45pyJ5pu06KqY5Lq655qE6KGM6Yq3562W55Wl44CC5Zug5q2k5oiR5YCR5oeJ6Kmy6KeA5a+f5YW25LuW6Iiq56m65YWs5Y+45pyJ5LuA6bq854ef6Yq35omL5rOV77yM54S25b6M5o6h5Y+W5pyJ6Yed5bCN5oCn55qE54ef6Yq35omL5q6177yM5bCH5rKI552h5a6i5oi25Zaa6YaS44CCDQoNCisg5YWs5Y+45LiN6ZyA6KaB6Iqx6LK76LOH5rqQ5Zyo6YCZ5Lqb5a6i5oi25LiK44CCDQoNCg0K57Wx6KiI5LiK5pyA5aW955qE5YiG576k5Lmf5piv5a+m5YuZ5LiK5pyA5aW955qE5YiG576k5ZeO77yfIA0KDQorIOS4puS4jeaYr+OAgue1seioiOS4iumBuOWHuueahOiuiuaVuO+8jOacgOWlveeahOWIhue+pOS4jeS4gOWumumBqeeUqOaWvOWvpuWLmeS4iu+8jOS5n+S4jeS4gOWumuWwjeWvpuWLmeacieiyoueNu+OAgg0KDQrpmaTkuobogIPmha7nvqTplpPlkoznvqTplpPot53pm6LkuYvlpJbvvIzlr6bli5nkuIrnmoTliIbnvqTpgJrluLjpgoTpnIDopoHogIPmha7pgqPkupvlm6DmlbjvvJ8gDQoNCisg5YiG57WE5pW46YeP5pyJ5pmC5Lmf5piv5b+F6aCI6ICD5oWu55qE5Zug57Sg77yM5Zug54K65pyJ5pmC5YiG57WE5pyA5b6M55qE57WQ5p6c5pyD6K6T5Lq66Zuj5Lul6Kej6YeL44CCDQrpgJnmmYLlj6/ku6XmlLnororliIbntYTmlbjph4/vvIzkuZ/oqLHkuI3lkIznmoTliIbms5XmnIPorpPkvaDnnIvliLDkuI3lkIznmoTop4Dpu57jgIINCg0KDQoNCg0KLSAtIC0NCg0KPGJyPjxicj48YnI+PGJyPjxicj4NCg0KPHN0eWxlPg0KLmNhcHRpb24gew0KICBjb2xvcjogIzc3NzsNCiAgbWFyZ2luLXRvcDogMTBweDsNCn0NCnAgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcHJlIHsNCiAgd29yZC1icmVhazogbm9ybWFsOw0KICB3b3JkLXdyYXA6IG5vcm1hbDsNCiAgbGluZS1oZWlnaHQ6IDE7DQp9DQpwcmUgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcCxsaSB7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQoucnsNCiAgbGluZS1oZWlnaHQ6IDEuMjsNCn0NCg0KdGl0bGV7DQogIGNvbG9yOiAjY2MwMDAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KYm9keXsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgxLGgyLGgzLGg0LGg1ew0KICBjb2xvcjogIzAwODgwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgzew0KICBjb2xvcjogI2IzNmIwMDsNCiAgYmFja2dyb3VuZDogI2ZmZTBiMzsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoNXsNCiAgY29sb3I6ICMwMDYwMDA7DQogIGJhY2tncm91bmQ6ICNmZmZmZTA7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KZW17DQogIGNvbG9yOiAjMDAwMGMwOw0KICBiYWNrZ3JvdW5kOiAjZjBmMGYwOw0KICB9DQo8L3N0eWxlPg0KDQo=