List of R colours: http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf

Cluster Analysis - Part II

Example 1 - Continued from Assignment 8

  1. Create 12 data vectors to represent each of the following. \[ \underline{a}=(3986,1943,444)\\ \underline{b}=(4271,1444,344)\\ \underline{c}=(8443,2020,415)\\ \underline{d}=(1898,1877,311)\\ \underline{e}=(2916,1253,289)\\ \underline{f}=(7476,1583,347)\\ \underline{g}=(3690,2131,491)\\ \underline{h}=(1355,1837,265)\\ \underline{i}=(7251,1492,306)\\ \underline{j}=(6498,1683,405)\\ \underline{k}=(5211,1298,367)\\ \underline{l}=(8001,1950,392)\\ \]

  2. Use the R-function rbind() to create a matrix representing these vectors in a single data structure.

  3. Use the scale()-function, so that all vector dimensions are of similar size.

  4. Create a scatter plot of the data in dimensions 1 and 2 of this scaled data structure.

  5. Using this scatter plot, identify any potential clusters among the 12 vectors.

Solution

Part 1. We begin by creating a list of data structures corresponding to the data objects given.

a<-c(3986,1943,444)
b<-c(4271,1444,344)
c<-c(8443,2020,415)
d<-c(1898,1877,311)
e<-c(2916,1253,289)
f<-c(7476,1583,347)
g<-c(3690,2131,491)
h<-c(1355,1837,265)
i<-c(7251,1492,306)
j<-c(6498,1683,405)
k<-c(5211,1298,367)
l<-c(8001,1950,392)

Part 2. The single data structure is given by:

Data<-rbind(a,b,c,d,e,f,g,h,i,j,k,l)
Data
  [,1] [,2] [,3]
a 3986 1943  444
b 4271 1444  344
c 8443 2020  415
d 1898 1877  311
e 2916 1253  289
f 7476 1583  347
g 3690 2131  491
h 1355 1837  265
i 7251 1492  306
j 6498 1683  405
k 5211 1298  367
l 8001 1950  392

Part 3. The scaled data structure is given by:

Data_S<-scale(Data,center=F,scale=c(1000,1000,100))
Data_S
   [,1]  [,2] [,3]
a 3.986 1.943 4.44
b 4.271 1.444 3.44
c 8.443 2.020 4.15
d 1.898 1.877 3.11
e 2.916 1.253 2.89
f 7.476 1.583 3.47
g 3.690 2.131 4.91
h 1.355 1.837 2.65
i 7.251 1.492 3.06
j 6.498 1.683 4.05
k 5.211 1.298 3.67
l 8.001 1.950 3.92
attr(,"scaled:scale")
[1] 1000 1000  100

Part 4. The scatter plot of the scaled data structure in dimensions 1 and 2 is given by:

plot(Data_S[,1],Data_S[,2],col='steelblue',pch=15,xlab='Scaled Dimension 1',ylab='Sclaed Dimension 2',main='Scatter Plot for Data in Dimensions 1 and 2')
text(x=Data_S[,1]+0.2,y=Data_S[,2],labels=c('a','b','c','d','e','f','g','h','i','j','k','l'),col='darkslategray')

Part 5. It appears from this scatter plot that the data is clustered as \[(a,g), (b,k), (c,l), (d,h), e, (f,i,j).\]

Remark: * While this clustering appears natural when we use the scatter plot, we must also consider that we are only comparing the data vectors along two dimensions.

  • When we compare the data vectors along another two dimensions we may get a completely different clustering.

  • When we perform a full cluster analysis, we take into account all dimensions of the objects in the data-set.

Exercise 1

Eight makes of car were compared using three different criteria:

  1. Price (euro) 2. Engine (cc) 3. Efficiency (km/L)

The data collected are given in the table below:

Make Price (euro) Engine (cc) Efficiency
Audi 38812 1968 22.7
BMW 35571 1995 23.7
Citroen 20451 1560 24.7
Hyundai 23620 1685 23.7
Jaguar 53693 1999 26.5
Mercedes 41909 1950 25.5
Mitsubishi 28192 2268 18.8
Toyota 27978 1995 21.6

Using the data in this table, answer the following:

  1. Create 8 data vectors to represent each car make.

  2. Combine these data vectors using the rbind() function, to create a single data structure.

  3. Rescale the dimensions of this data stricture, so each dimension is measured in the same order.

  4. Create a scatter-plot of this data along dimensions 1 and 2 of this data set.

  5. Use this scatter plot to identify potential clusters in the data-set.

  6. Create a scatter-plot of this data along dimensions 1 and 3 of this data set.

  7. Use this scatter plot to identify potential clusters in the data-set.

  8. Do the clusters match?

Clustering Algorithms

  • To implement a clustering algorithm on a set of data vectors, we must find the distances between those data vectors, using a metric such as the Euclidean or Manhattan metric.

  • Once we have these distances we can use a clustering algorithm such as Complete Linkage or Single Linkage to identify the clusters in the data-set.

Example 2:

Using the scaled data-frame from Example 1, answer the following:

  1. Find the Manhattan distances between these data vectors.

  2. Using these Manhattan distances, use the function hclust() to perform a cluster analysis on this data using the Single Linkage method.

  3. Use the function fvis_dend() to create a dendrogram for the data with 2,4,6 and 8 clusters.

Solution:

Part 1: The Manhattan distances between the scaled data vectors are given by

Dist_S_M=dist(Data_S,method='manhattan')
Dist_S_M
      a     b     c     d     e     f     g     h     i     j     k
b 1.784                                                            
c 4.824 5.458                                                      
d 3.484 3.136 7.728                                                
e 3.310 2.096 7.554 1.862                                          
f 4.820 3.374 2.084 6.232 5.470                                    
g 0.954 2.738 5.624 3.846 3.672 5.774                              
h 4.527 4.099 8.771 1.043 2.385 7.195 4.889                        
i 5.096 3.408 2.810 5.788 4.744 0.726 6.050 6.651                  
j 3.162 3.076 2.382 5.734 5.172 1.658 4.116 6.697 1.934            
k 2.640 1.316 4.434 4.452 3.120 2.750 3.594 5.415 2.844 2.052      
l 4.542 4.716 0.742 6.986 6.812 1.342 5.482 8.029 2.068 1.900 3.692

Part 2: The clustering is performed using the function hclust(), which is available from the library cluster.

  • Importing the library:
library(cluster)
  • Performing the clustering using Single Linkage
Cluster_1_M=hclust(Dist_S_M,method='single')
Cluster_1_M

Call:
hclust(d = Dist_S_M, method = "single")

Cluster method   : single 
Distance         : manhattan 
Number of objects: 12 

Part 3: The function fviz_dend() is available in the factoextra library, which we import as follows:

library(factoextra)
  • The cluster dendrogram with 2 clusters is given by
fviz_dend(Cluster_1_M,k=2,color_labels_by_k=T, rect=T, k_colors=c("red", "dodgerblue"))

  • The cluster dendrogram with 4 clusters is given by
fviz_dend(Cluster_1_M,k=4,color_labels_by_k=T, rect=T, k_colors=c("red", "dodgerblue",'seagreen4','goldenrod'))

  • The cluster dendrogram with 6 clusters is given by
fviz_dend(Cluster_1_M,k=6,color_labels_by_k=T, rect=T, k_colors=c("red", "dodgerblue",'seagreen4','goldenrod','azure4','coral'))

  • The cluster dendrogram with 8 clusters is given by
fviz_dend(Cluster_1_M,k=8,color_labels_by_k=T, rect=T, k_colors=c("red", "dodgerblue",'seagreen4','goldenrod','azure4','coral', 'purple', 'firebrick'))

Exercise 2:

Using the scaled car-data vectors created in in Exercise 1, answer the following:

  1. Find the Manhattan distances between these data vectors.

  2. Using these Manhattan distances, use the function hclust() to perform a cluster analysis on this data using the Complete Linkage method.

  3. Use the function fvis_dend() to create a dendrogram for the data with 2,4,6 and 8 clusters.

Exercise 3:

Six makes of laptop we compared using three different criteria:

  1. Storage (GB) 2. Screen (inches) 3. Ram (GB) 4. Clock-speed (GHz)

The data is available at

Moodle \(\rightarrow\) Data Visualisation \(\rightarrow\) Data Files \(\rightarrow\) LaptopData

  • Import this data using read.csv() in the usual way.
Laptops<-read.csv(file.choose())
Laptops
  • Modify this data structure so that it can used by the function dist() (see Assignment 8).

Step 1

LaptopNames<-as.character(Laptops$Make)
LaptopNames
[1] "Dell Inspiron"    "Acer Aspire"      "LG Gram"          "Lenovo Yoga"      "Google Pixelbook" "Dell i3168"      

Step 2

LaptopData<-as.data.frame(Laptops[,-1],row.names=LaptopNames)
LaptopData

Using this data structure answer the following:

  1. Rescale this new data structure so that all dimensions have values between 0 and 10.

  2. Find the Euclidean and Manhattan Distances between these scaled vectors.

  3. Apply the clustering algorithm using Complete Linkage to both of these distance sets.

  4. Plot a dedrogram with 2, 3, 4 and 5 data clusters obtained using each metric.

Exercise 4:

On Moodle, the file EuroZoneData2017.csv compares the countries of the Euro Zone (excluding Malta as not all data was available), using 4 different criteria

  1. Population 2. GDP per capita (US$) 3. Total Exports (US$) 4. Total Imports (US$)

Moodle \(\rightarrow\) Data Visualisation \(\rightarrow\) Workbook Files \(\rightarrow\) EuroZoneData2017.csv

(Source: http://www.worldbank.org)

Using this data answer the following:

  1. Import the data into this R workbook using read.csv().

  2. Create an appropriate data frame from this data file which can be used by the dist() function.

  3. Rescale the data columns of this new data frame so that all dimensions have a similar size.

  4. Find the Manhattan distances using this re-scaled data frame.

  5. Apply the clustering algorithms Complete Linkage and Single Linkage to both of these distances.

  6. Plot a dedrogram with 2, 10, 15 and 20 data clusters obtained using both clustering methods.

LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpc2F0aW9uIDIwMTkgLSBBc3NpZ25tZW50IDkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyMgIExpc3Qgb2YgUiBjb2xvdXJzOiBodHRwOi8vd3d3LnN0YXQuY29sdW1iaWEuZWR1L350emhlbmcvZmlsZXMvUmNvbG9yLnBkZgoKIyBDbHVzdGVyIEFuYWx5c2lzIC0gUGFydCBJSQogICAgCiMjIyBFeGFtcGxlIDEgLSBDb250aW51ZWQgZnJvbSBBc3NpZ25tZW50IDgKCjEuIENyZWF0ZSBfXzEyX18gZGF0YSB2ZWN0b3JzIHRvIHJlcHJlc2VudCBlYWNoIG9mIHRoZSBmb2xsb3dpbmcuClxbClx1bmRlcmxpbmV7YX09KDM5ODYsMTk0Myw0NDQpXFwKXHVuZGVybGluZXtifT0oNDI3MSwxNDQ0LDM0NClcXApcdW5kZXJsaW5le2N9PSg4NDQzLDIwMjAsNDE1KVxcClx1bmRlcmxpbmV7ZH09KDE4OTgsMTg3NywzMTEpXFwKXHVuZGVybGluZXtlfT0oMjkxNiwxMjUzLDI4OSlcXApcdW5kZXJsaW5le2Z9PSg3NDc2LDE1ODMsMzQ3KVxcClx1bmRlcmxpbmV7Z309KDM2OTAsMjEzMSw0OTEpXFwKXHVuZGVybGluZXtofT0oMTM1NSwxODM3LDI2NSlcXApcdW5kZXJsaW5le2l9PSg3MjUxLDE0OTIsMzA2KVxcClx1bmRlcmxpbmV7an09KDY0OTgsMTY4Myw0MDUpXFwKXHVuZGVybGluZXtrfT0oNTIxMSwxMjk4LDM2NylcXApcdW5kZXJsaW5le2x9PSg4MDAxLDE5NTAsMzkyKVxcClxdCgoyLiBVc2UgdGhlIF9fUl9fLWZ1bmN0aW9uIF9fcmJpbmQoKV9fICB0byBjcmVhdGUgYSBfX21hdHJpeF9fIHJlcHJlc2VudGluZyB0aGVzZSB2ZWN0b3JzIGluIGEgc2luZ2xlIGRhdGEgc3RydWN0dXJlLiAgCgozLiAgVXNlIHRoZSBfX3NjYWxlKClfXy1mdW5jdGlvbiwgc28gdGhhdCBhbGwgdmVjdG9yIGRpbWVuc2lvbnMgYXJlIG9mIHNpbWlsYXIgc2l6ZS4KCjQuIENyZWF0ZSBhIF9fc2NhdHRlciBwbG90X18gb2YgdGhlIGRhdGEgaW4gZGltZW5zaW9ucyAxIGFuZCAyIG9mIHRoaXMgc2NhbGVkIGRhdGEgc3RydWN0dXJlLgoKNS4gVXNpbmcgdGhpcyBzY2F0dGVyIHBsb3QsIGlkZW50aWZ5IGFueSBfX3BvdGVudGlhbF9fIGNsdXN0ZXJzIGFtb25nIHRoZSAxMiB2ZWN0b3JzLgoKIyMjIFNvbHV0aW9uCgpfX1BhcnQgMS5fXyBXZSBiZWdpbiBieSBjcmVhdGluZyBhIGxpc3Qgb2YgZGF0YSBzdHJ1Y3R1cmVzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGRhdGEgb2JqZWN0cyBnaXZlbi4KCmBgYHtyfQphPC1jKDM5ODYsMTk0Myw0NDQpCmI8LWMoNDI3MSwxNDQ0LDM0NCkKYzwtYyg4NDQzLDIwMjAsNDE1KQpkPC1jKDE4OTgsMTg3NywzMTEpCmU8LWMoMjkxNiwxMjUzLDI4OSkKZjwtYyg3NDc2LDE1ODMsMzQ3KQpnPC1jKDM2OTAsMjEzMSw0OTEpCmg8LWMoMTM1NSwxODM3LDI2NSkKaTwtYyg3MjUxLDE0OTIsMzA2KQpqPC1jKDY0OTgsMTY4Myw0MDUpCms8LWMoNTIxMSwxMjk4LDM2NykKbDwtYyg4MDAxLDE5NTAsMzkyKQpgYGAKCl9fUGFydCAyLl9fIFRoZSBfX3NpbmdsZV9fIGRhdGEgc3RydWN0dXJlIGlzIGdpdmVuIGJ5OgoKYGBge3J9CkRhdGE8LXJiaW5kKGEsYixjLGQsZSxmLGcsaCxpLGosayxsKQpEYXRhCmBgYAoKX19QYXJ0IDMuX18gVGhlIF9fc2NhbGVkX18gZGF0YSBzdHJ1Y3R1cmUgaXMgZ2l2ZW4gYnk6CgpgYGB7cn0KRGF0YV9TPC1zY2FsZShEYXRhLGNlbnRlcj1GLHNjYWxlPWMoMTAwMCwxMDAwLDEwMCkpCkRhdGFfUwpgYGAKCgoKX19QYXJ0IDQuX18gIFRoZSBfX3NjYXR0ZXIgcGxvdF9fIG9mIHRoZSBzY2FsZWQgZGF0YSBzdHJ1Y3R1cmUgaW4gZGltZW5zaW9ucyAxIGFuZCAyIGlzIGdpdmVuIGJ5OgoKYGBge3J9CnBsb3QoRGF0YV9TWywxXSxEYXRhX1NbLDJdLGNvbD0nc3RlZWxibHVlJyxwY2g9MTUseGxhYj0nU2NhbGVkIERpbWVuc2lvbiAxJyx5bGFiPSdTY2FsZWQgRGltZW5zaW9uIDInLG1haW49J1NjYXR0ZXIgUGxvdCBmb3IgRGF0YSBpbiBEaW1lbnNpb25zIDEgYW5kIDInKQp0ZXh0KHg9RGF0YV9TWywxXSswLjIseT1EYXRhX1NbLDJdLGxhYmVscz1jKCdhJywnYicsJ2MnLCdkJywnZScsJ2YnLCdnJywnaCcsJ2knLCdqJywnaycsJ2wnKSxjb2w9J2RhcmtzbGF0ZWdyYXknKQpgYGAKCl9fUGFydCA1Ll9fIApJdCBhcHBlYXJzIGZyb20gdGhpcyBzY2F0dGVyIHBsb3QgdGhhdCB0aGUgZGF0YSBpcyBjbHVzdGVyZWQgYXMKXFsoYSxnKSwgKGIsayksIChjLGwpLCAoZCxoKSwgZSwgKGYsaSxqKS5cXQoKX19SZW1hcms6X18gCiAgKiBXaGlsZSB0aGlzIGNsdXN0ZXJpbmcgYXBwZWFycyBuYXR1cmFsIHdoZW4gd2UgdXNlIHRoZSBzY2F0dGVyIHBsb3QsIHdlIG11c3QgYWxzbyBjb25zaWRlciB0aGF0IHdlIGFyZSBvbmx5IGNvbXBhcmluZyB0aGUgZGF0YSB2ZWN0b3JzIGFsb25nIF9fdHdvX18gICBkaW1lbnNpb25zLiAKICAKICAqIFdoZW4gd2UgY29tcGFyZSB0aGUgZGF0YSB2ZWN0b3JzIGFsb25nIGFub3RoZXIgdHdvIGRpbWVuc2lvbnMgd2UgX19tYXlfXyBnZXQgYSBjb21wbGV0ZWx5IGRpZmZlcmVudCBjbHVzdGVyaW5nLgogIAogICogV2hlbiB3ZSBwZXJmb3JtIGEgZnVsbCBjbHVzdGVyIGFuYWx5c2lzLCB3ZSB0YWtlIGludG8gYWNjb3VudCBhbGwgZGltZW5zaW9ucyBvZiB0aGUgb2JqZWN0cyBpbiB0aGUgZGF0YS1zZXQuCgoKIyMjIEV4ZXJjaXNlIDEKCkVpZ2h0IG1ha2VzIG9mIGNhciB3ZXJlIGNvbXBhcmVkIHVzaW5nIHRocmVlIGRpZmZlcmVudCBjcml0ZXJpYToKCgogIDEuIFByaWNlIChldXJvKSAgICAyLiBFbmdpbmUgKGNjKSAgICAzLiBFZmZpY2llbmN5IChrbS9MKQoKVGhlIGRhdGEgY29sbGVjdGVkIGFyZSBnaXZlbiBpbiB0aGUgdGFibGUgYmVsb3c6Cgp8IE1ha2UgICAgICAgfCBQcmljZSAoZXVybykgfCBFbmdpbmUgKGNjKSB8IEVmZmljaWVuY3kgfAp8LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfAp8IEF1ZGkgICAgICAgfCAzODgxMiAgICAgfCAgICAgMTk2OCAgICB8ICAgICAyMi43ICAgfAp8IEJNVyAgICAgICAgfCAzNTU3MSAgICAgfCAgICAgMTk5NSAgICB8ICAgICAyMy43ICAgfAp8IENpdHJvZW4gICAgfCAyMDQ1MSAgICAgfCAgICAgMTU2MCAgICB8ICAgICAyNC43ICAgfAp8IEh5dW5kYWkgICAgfCAyMzYyMCAgICAgfCAgICAgMTY4NSAgICB8ICAgICAyMy43ICAgfAp8IEphZ3VhciAgICAgfCA1MzY5MyAgICAgfCAgICAgMTk5OSAgICB8ICAgICAyNi41ICAgfAp8IE1lcmNlZGVzICAgfCA0MTkwOSAgICAgfCAgICAgMTk1MCAgICB8ICAgICAyNS41ICAgfAp8IE1pdHN1YmlzaGkgfCAyODE5MiAgICAgfCAgICAgMjI2OCAgICB8ICAgICAxOC44ICAgfAp8IFRveW90YSAgICAgfCAyNzk3OCAgICAgfCAgICAgMTk5NSAgICB8ICAgICAyMS42ICAgfAoKVXNpbmcgdGhlIGRhdGEgaW4gdGhpcyB0YWJsZSwgYW5zd2VyIHRoZSBmb2xsb3dpbmc6CgoxLiBDcmVhdGUgOCBfX2RhdGEgdmVjdG9yc19fIHRvIHJlcHJlc2VudCBlYWNoIGNhciBtYWtlLgoKMi4gQ29tYmluZSB0aGVzZSBkYXRhIHZlY3RvcnMgdXNpbmcgdGhlIF9fcmJpbmQoKV9fIGZ1bmN0aW9uLCB0byBjcmVhdGUgYSBzaW5nbGUgX19kYXRhIHN0cnVjdHVyZV9fLgoKMy4gUmVzY2FsZSB0aGUgZGltZW5zaW9ucyBvZiB0aGlzIGRhdGEgc3RyaWN0dXJlLCBzbyBlYWNoIGRpbWVuc2lvbiBpcyBtZWFzdXJlZCBpbiB0aGUgc2FtZSBvcmRlci4KCjQuIENyZWF0ZSBhIHNjYXR0ZXItcGxvdCBvZiB0aGlzIGRhdGEgYWxvbmcgZGltZW5zaW9ucyBfXzFfXyBhbmQgX18yX18gb2YgdGhpcyBkYXRhIHNldC4KCjUuIFVzZSB0aGlzIHNjYXR0ZXIgcGxvdCB0byBpZGVudGlmeSBwb3RlbnRpYWwgY2x1c3RlcnMgaW4gdGhlIGRhdGEtc2V0LgoKNi4gQ3JlYXRlIGEgc2NhdHRlci1wbG90IG9mIHRoaXMgZGF0YSBhbG9uZyBkaW1lbnNpb25zIF9fMV9fIGFuZCBfXzNfXyBvZiB0aGlzIGRhdGEgc2V0LgoKNy4gVXNlIHRoaXMgc2NhdHRlciBwbG90IHRvIGlkZW50aWZ5IHBvdGVudGlhbCBjbHVzdGVycyBpbiB0aGUgZGF0YS1zZXQuCgo4LiBEbyB0aGUgY2x1c3RlcnMgbWF0Y2g/CgoKIyMgQ2x1c3RlcmluZyBBbGdvcml0aG1zCgoqIFRvIGltcGxlbWVudCBhIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIG9uIGEgc2V0IG9mIGRhdGEgdmVjdG9ycywgd2UgbXVzdCBmaW5kIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiB0aG9zZSBkYXRhIHZlY3RvcnMsIHVzaW5nIGEgbWV0cmljIHN1Y2ggYXMgdGhlIEV1Y2xpZGVhbiBvciBNYW5oYXR0YW4gbWV0cmljLgoKKiBPbmNlIHdlIGhhdmUgdGhlc2UgZGlzdGFuY2VzIHdlIGNhbiB1c2UgYSBjbHVzdGVyaW5nIGFsZ29yaXRobSBzdWNoIGFzIF9fQ29tcGxldGUgTGlua2FnZV9fIG9yIF9fU2luZ2xlIExpbmthZ2VfXyB0byBpZGVudGlmeSB0aGUgY2x1c3RlcnMgaW4gdGhlIGRhdGEtc2V0LgoKIyMjIEV4YW1wbGUgMjoKClVzaW5nIHRoZSBzY2FsZWQgZGF0YS1mcmFtZSBmcm9tIF9fRXhhbXBsZSAxX18sIGFuc3dlciB0aGUgZm9sbG93aW5nOgoKICAxLiBGaW5kIHRoZSBfX01hbmhhdHRhbl9fIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZXNlIGRhdGEgdmVjdG9ycy4KICAKICAyLiBVc2luZyB0aGVzZSBNYW5oYXR0YW4gZGlzdGFuY2VzLCB1c2UgdGhlIGZ1bmN0aW9uIF9faGNsdXN0KClfXyB0byBwZXJmb3JtIGEgY2x1c3RlciBhbmFseXNpcyBvbiB0aGlzIGRhdGEgdXNpbmcgdGhlIF9fU2luZ2xlIExpbmthZ2VfXyBtZXRob2QuCiAgCiAgMy4gVXNlIHRoZSBmdW5jdGlvbiBfX2Z2aXNfZGVuZCgpX18gdG8gY3JlYXRlIGEgX19kZW5kcm9ncmFtX18gZm9yIHRoZSBkYXRhIHdpdGggMiw0LDYgYW5kIDggY2x1c3RlcnMuCiAgCl9Tb2x1dGlvbjpfCgpfX1BhcnQgMTpfXyBUaGUgTWFuaGF0dGFuIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZSBzY2FsZWQgZGF0YSB2ZWN0b3JzIGFyZSBnaXZlbiBieQoKYGBge3J9CkRpc3RfU19NPWRpc3QoRGF0YV9TLG1ldGhvZD0nbWFuaGF0dGFuJykKRGlzdF9TX00KYGBgCiAgCl9fUGFydCAyOl9fIFRoZSBjbHVzdGVyaW5nIGlzIHBlcmZvcm1lZCB1c2luZyB0aGUgZnVuY3Rpb24gX19oY2x1c3QoKV9fLCB3aGljaCBpcyBhdmFpbGFibGUgZnJvbSB0aGUgbGlicmFyeSBfX2NsdXN0ZXJfXy4KCiogSW1wb3J0aW5nIHRoZSBsaWJyYXJ5OgpgYGB7cn0KbGlicmFyeShjbHVzdGVyKQpgYGAKICAKKiBQZXJmb3JtaW5nIHRoZSBjbHVzdGVyaW5nIHVzaW5nIF9fU2luZ2xlIExpbmthZ2VfXyAKCmBgYHtyfQpDbHVzdGVyXzFfTT1oY2x1c3QoRGlzdF9TX00sbWV0aG9kPSdzaW5nbGUnKQpDbHVzdGVyXzFfTQpgYGAKICAKX19QYXJ0IDM6X18gVGhlIGZ1bmN0aW9uIF9fZnZpel9kZW5kKClfXyBpcyBhdmFpbGFibGUgaW4gdGhlIF9fZmFjdG9leHRyYV9fIGxpYnJhcnksIHdoaWNoIHdlIGltcG9ydCBhcyBmb2xsb3dzOgpgYGB7cn0KbGlicmFyeShmYWN0b2V4dHJhKQpgYGAKCiogVGhlIGNsdXN0ZXIgZGVuZHJvZ3JhbSB3aXRoIF9fMiBjbHVzdGVyc19fIGlzIGdpdmVuIGJ5CmBgYHtyfQpmdml6X2RlbmQoQ2x1c3Rlcl8xX00saz0yLGNvbG9yX2xhYmVsc19ieV9rPVQsIHJlY3Q9VCwga19jb2xvcnM9YygicmVkIiwgImRvZGdlcmJsdWUiKSkKYGBgCgoqIFRoZSBjbHVzdGVyIGRlbmRyb2dyYW0gd2l0aCBfXzQgY2x1c3RlcnNfXyBpcyBnaXZlbiBieQpgYGB7cn0KZnZpel9kZW5kKENsdXN0ZXJfMV9NLGs9NCxjb2xvcl9sYWJlbHNfYnlfaz1ULCByZWN0PVQsIGtfY29sb3JzPWMoInJlZCIsICJkb2RnZXJibHVlIiwnc2VhZ3JlZW40JywnZ29sZGVucm9kJykpCmBgYAoKKiBUaGUgY2x1c3RlciBkZW5kcm9ncmFtIHdpdGggX182IGNsdXN0ZXJzX18gaXMgZ2l2ZW4gYnkKYGBge3J9CmZ2aXpfZGVuZChDbHVzdGVyXzFfTSxrPTYsY29sb3JfbGFiZWxzX2J5X2s9VCwgcmVjdD1ULCBrX2NvbG9ycz1jKCJyZWQiLCAiZG9kZ2VyYmx1ZSIsJ3NlYWdyZWVuNCcsJ2dvbGRlbnJvZCcsJ2F6dXJlNCcsJ2NvcmFsJykpCmBgYAoKKiBUaGUgY2x1c3RlciBkZW5kcm9ncmFtIHdpdGggX184IGNsdXN0ZXJzX18gaXMgZ2l2ZW4gYnkKYGBge3J9CmZ2aXpfZGVuZChDbHVzdGVyXzFfTSxrPTgsY29sb3JfbGFiZWxzX2J5X2s9VCwgcmVjdD1ULCBrX2NvbG9ycz1jKCJyZWQiLCAiZG9kZ2VyYmx1ZSIsJ3NlYWdyZWVuNCcsJ2dvbGRlbnJvZCcsJ2F6dXJlNCcsJ2NvcmFsJywgJ3B1cnBsZScsICdmaXJlYnJpY2snKSkKYGBgCgoKIyMjIEV4ZXJjaXNlIDI6ClVzaW5nIHRoZSAgc2NhbGVkIGNhci1kYXRhIHZlY3RvcnMgY3JlYXRlZCBpbiBpbiBfX0V4ZXJjaXNlIDFfXywgYW5zd2VyIHRoZSBmb2xsb3dpbmc6CgogIDEuIEZpbmQgdGhlIF9fTWFuaGF0dGFuX18gZGlzdGFuY2VzIGJldHdlZW4gdGhlc2UgZGF0YSB2ZWN0b3JzLgogIAogIDIuIFVzaW5nIHRoZXNlIE1hbmhhdHRhbiBkaXN0YW5jZXMsIHVzZSB0aGUgZnVuY3Rpb24gX19oY2x1c3QoKV9fIHRvIHBlcmZvcm0gYSBjbHVzdGVyIGFuYWx5c2lzIG9uIHRoaXMgZGF0YSB1c2luZyB0aGUgX19Db21wbGV0ZSBMaW5rYWdlX18gbWV0aG9kLgogIAogIDMuIFVzZSB0aGUgZnVuY3Rpb24gX19mdmlzX2RlbmQoKV9fIHRvIGNyZWF0ZSBhIF9fZGVuZHJvZ3JhbV9fIGZvciB0aGUgZGF0YSB3aXRoIDIsNCw2IGFuZCA4IGNsdXN0ZXJzLgoKICAKCgojIyMgRXhlcmNpc2UgMzoKClNpeCBtYWtlcyBvZiBsYXB0b3Agd2UgY29tcGFyZWQgdXNpbmcgdGhyZWUgZGlmZmVyZW50IGNyaXRlcmlhOgoKICAxLiBTdG9yYWdlIChHQikgMi4gU2NyZWVuIChpbmNoZXMpICAzLiBSYW0gKEdCKSA0LiBDbG9jay1zcGVlZCAoR0h6KQogIApUaGUgZGF0YSBpcyBhdmFpbGFibGUgYXQgCgogIF9fTW9vZGxlX18gJFxyaWdodGFycm93JCBfX0RhdGEgVmlzdWFsaXNhdGlvbl9fICRccmlnaHRhcnJvdyQgX19EYXRhIEZpbGVzX18gJFxyaWdodGFycm93JCBfX0xhcHRvcERhdGFfXwogIAoqIEltcG9ydCB0aGlzIGRhdGEgdXNpbmcgX19yZWFkLmNzdigpX18gaW4gdGhlIHVzdWFsIHdheS4KCmBgYHtyfQpMYXB0b3BzPC1yZWFkLmNzdihmaWxlLmNob29zZSgpKQpMYXB0b3BzCmBgYAoKCgoqIE1vZGlmeSB0aGlzIGRhdGEgc3RydWN0dXJlIHNvIHRoYXQgaXQgY2FuIHVzZWQgYnkgdGhlIGZ1bmN0aW9uIF9fZGlzdCgpX18gKHNlZSBBc3NpZ25tZW50IDgpLgoKX1N0ZXAgMV8KYGBge3J9CkxhcHRvcE5hbWVzPC1hcy5jaGFyYWN0ZXIoTGFwdG9wcyRNYWtlKQpMYXB0b3BOYW1lcwpgYGAgICAgCl9TdGVwIDJfCmBgYHtyfQpMYXB0b3BEYXRhPC1hcy5kYXRhLmZyYW1lKExhcHRvcHNbLC0xXSxyb3cubmFtZXM9TGFwdG9wTmFtZXMpCkxhcHRvcERhdGEKYGBgCgpVc2luZyB0aGlzIGRhdGEgc3RydWN0dXJlIGFuc3dlciB0aGUgZm9sbG93aW5nOgoKIDEuIFJlc2NhbGUgdGhpcyBuZXcgZGF0YSBzdHJ1Y3R1cmUgc28gdGhhdCBhbGwgZGltZW5zaW9ucyBoYXZlIHZhbHVlcyBiZXR3ZWVuIDAgYW5kIDEwLgogCiAyLiBGaW5kIHRoZSBFdWNsaWRlYW4gYW5kIE1hbmhhdHRhbiBEaXN0YW5jZXMgYmV0d2VlbiB0aGVzZSBzY2FsZWQgdmVjdG9ycy4KIAogMy4gQXBwbHkgdGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIHVzaW5nIF9fQ29tcGxldGUgTGlua2FnZV9fIHRvIGJvdGggb2YgdGhlc2UgZGlzdGFuY2Ugc2V0cy4KIAogNC4gUGxvdCBhIF9fZGVkcm9ncmFtX18gd2l0aCAyLCAzLCA0IGFuZCA1IGRhdGEgY2x1c3RlcnMgb2J0YWluZWQgdXNpbmcgZWFjaCBtZXRyaWMuCiAKIAoKCiMjIyBFeGVyY2lzZSA0OgoKT24gTW9vZGxlLCB0aGUgZmlsZSBfX0V1cm9ab25lRGF0YTIwMTcuY3N2X18gIGNvbXBhcmVzIHRoZSBjb3VudHJpZXMgb2YgdGhlIEV1cm8gWm9uZSAoZXhjbHVkaW5nIE1hbHRhIGFzIG5vdCBhbGwgZGF0YSB3YXMgYXZhaWxhYmxlKSwgdXNpbmcgNCBkaWZmZXJlbnQgY3JpdGVyaWEKCiAgMS4gUG9wdWxhdGlvbiAyLiBHRFAgcGVyIGNhcGl0YSAoVVNcJCkgMy4gVG90YWwgRXhwb3J0cyAoVVNcJCkgNC4gVG90YWwgSW1wb3J0cyAoVVNcJCkKCl9fTW9vZGxlX18gJFxyaWdodGFycm93JCBfX0RhdGEgVmlzdWFsaXNhdGlvbl9fICRccmlnaHRhcnJvdyQgX19Xb3JrYm9vayBGaWxlc19fICRccmlnaHRhcnJvdyQgX19FdXJvWm9uZURhdGEyMDE3LmNzdl9fIAoKKF9Tb3VyY2U6XyBodHRwOi8vd3d3LndvcmxkYmFuay5vcmcpCgpVc2luZyB0aGlzIGRhdGEgYW5zd2VyIHRoZSBmb2xsb3dpbmc6CgoxLiBJbXBvcnQgdGhlIGRhdGEgaW50byB0aGlzIF9fUl9fIHdvcmtib29rIHVzaW5nIF9fcmVhZC5jc3YoKV9fLgoKMi4gQ3JlYXRlIGFuIGFwcHJvcHJpYXRlIGRhdGEgZnJhbWUgZnJvbSB0aGlzIGRhdGEgZmlsZSB3aGljaCBjYW4gYmUgdXNlZCBieSB0aGUgX19kaXN0KClfXyBmdW5jdGlvbi4KCjMuIFJlc2NhbGUgdGhlIGRhdGEgY29sdW1ucyBvZiB0aGlzIG5ldyBkYXRhIGZyYW1lIHNvIHRoYXQgYWxsIGRpbWVuc2lvbnMgaGF2ZSBhIHNpbWlsYXIgc2l6ZS4KCjQuIEZpbmQgdGhlIE1hbmhhdHRhbiBkaXN0YW5jZXMgdXNpbmcgdGhpcyByZS1zY2FsZWQgZGF0YSBmcmFtZS4KCjUuIEFwcGx5IHRoZSBjbHVzdGVyaW5nIGFsZ29yaXRobXMgX19Db21wbGV0ZSBMaW5rYWdlX18gYW5kIF9fU2luZ2xlIExpbmthZ2VfXyB0byBib3RoIG9mIHRoZXNlIGRpc3RhbmNlcy4KIAo2LiBQbG90IGEgX19kZWRyb2dyYW1fXyB3aXRoIDIsIDEwLCAxNSBhbmQgMjAgZGF0YSBjbHVzdGVycyBvYnRhaW5lZCB1c2luZyBib3RoIGNsdXN0ZXJpbmcgbWV0aG9kcy4gCgoKCg==