主要議題:社會網路簡介

學習重點:

rm(list=ls(all=T))
Error in names(frame) <- `*vtmp*` : names() applied to a non-vector
Sys.setlocale("LC_ALL","C")
[1] "C"
options(digits=4, scipen=12)
library(magrittr)
library(igraph)
library(rgl)


1 Summarizing the Data

1.1

Load the data from edges.csv into a data frame called edges, and load the data from users.csv into a data frame called users.

edges = read.csv('data/edges.csv')
users = read.csv('data/users.csv')

How many Facebook users are there in our dataset?

nrow(users)
[1] 59

In our dataset, what is the average number of friends per user? Hint: this question is tricky, and it might help to start by thinking about a small example with two users who are friends.

MIT
str(edges) 
'data.frame':   146 obs. of  2 variables:
 $ V1: int  4019 4023 4023 4027 3988 3982 3994 3998 3993 3982 ...
 $ V2: int  4026 4031 4030 4032 4021 3986 3998 3999 3995 4021 ...
nrow(edges)
[1] 146
146*2/59
[1] 4.949
#因為A是B的朋友,同時B也是A的朋友,總共有146對用戶是好友,每一對(A,B)需算兩次,因此146乘以2=292,再除以59
2*nrow(edges)/nrow(users)
[1] 4.949
1.2

Out of all the students who listed a school, what was the most common locale?

MIT
table(users$locale, users$school)
   
        A AB
     3  0  0
  A  6  0  0
  B 31 17  2
print("Locale B")
[1] "Locale B"
table(users$locale)

    A  B 
 3  6 50 
1.3

Is it possible that either school A or B is an all-girls or all-boys school?

table(gender=users$gender, school=users$school)
      school
gender     A AB
        1  1  0
     A 11  3  1
     B 28 13  1
print("No")
[1] "No"
#因為做出來性別A和B在學校A或B都有分布


2 Creating a Network

We will be using the igraph package to visualize networks; install and load this package using the install.packages and library commands.

2.1 Construct a Graph

We can create a new graph object using the graph.data.frame() function. Based on ?graph.data.frame,

library(igraph)
g = graph.data.frame(d=edges, directed=FALSE, vertices=users)

which of the following commands will create a graph g describing our social network, with the attributes of each user correctly loaded?

  • g = graph.data.frame(edges, FALSE, users)

Note: A directed graph is one where the edges only go one way – they point from one vertex to another. The other option is an undirected graph, which means that the relations between the vertices are symmetric.

2.2 Components

Use the correct command from Problem 2.1 to load the graph g.

Now, we want to plot our graph. By default, the vertices are large and have text labels of a user’s identifier. Because this would clutter the output, we will plot with no text labels and smaller vertices:

plot(g, vertex.size=5, vertex.label=NA)

In this graph, there are a number of groups of nodes where all the nodes in each group are connected but the groups are disjoint from one another, forming “islands” in the graph. Such groups are called “connected components,” or “components” for short. How many connected components with at least 2 nodes are there in the graph?

  • 4

How many users are there with no friends in the network? + 7 +

2.3 Degree

In our graph, the “degree” of a node is its number of friends. We have already seen that some nodes in our graph have degree 0 (these are the nodes with no friends), while others have much higher degree. We can use degree(g) to compute the degree of all the nodes in our graph g.

MIT
table(degree(g))

 0  1  2  3  4  5  6  7  8  9 10 11 13 17 18 
 7 10  4  9  1  4  4  3  6  2  4  1  2  1  1 
table(degree(g) >= 10)

FALSE  TRUE 
   50     9 
#10個以上的用戶:4+1+2+1+1=9

How many users are friends with 10 or more other Facebook users in this network?

sum(degree(g) >= 10)
[1] 9
2.4 Size by Degree

In a network, it’s often visually useful to draw attention to “important” nodes in the network. While this might mean different things in different contexts, in a social network we might consider a user with a large number of friends to be an important user. From the previous problem, we know this is the same as saying that nodes with a high degree are important users.

To visually draw attention to these nodes, we will change the size of the vertices so the vertices with high degrees are larger. To do this, we will change the “size” attribute of the vertices of our graph to be an increasing function of their degrees:

V(g)$size = degree(g)/2+2 
plot(g, vertex.label=NA)

#把有大量臉書朋友的頂點放大

What is the largest size we assigned to any node in our graph?
What is the smallest size we assigned to any node in our graph?

MIT
table(degree(g))

 0  1  2  3  4  5  6  7  8  9 10 11 13 17 18 
 7 10  4  9  1  4  4  3  6  2  4  1  2  1  1 
summary(degree(g))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    1.00    3.00    4.95    8.00   18.00 
range(V(g)$size)
[1]  2 11


3 Coloring Vertices

3.1 Colored by Gender

Thus far, we have changed the “size” attributes of our vertices. However, we can also change the colors of vertices to capture additional information about the Facebook users we are depicting.

When changing the size of nodes, we first obtained the vertices of our graph with V(g) and then accessed the the size attribute with V(g)\(size. To change the color, we will update the attribute `V(g)\)color`.

To color the vertices based on the gender of the user, we will need access to that variable. When we created our graph g, we provided it with the data frame users, which had variables gender, school, and locale. These are now stored as attributes V(g)\(gender, V(g)\)school, and V(g)$locale.

We can update the colors by setting the color to black for all vertices, than setting it to red for the vertices with gender A and setting it to gray for the vertices with gender B:

Plot the resulting graph.

V(g)$color = "black"
V(g)$color[V(g)$gender == "A"] = "red"
V(g)$color[V(g)$gender == "B"] = "gray"
plot(g, vertex.label=NA)

What is the gender of the users with the highest degree in the graph?

  • B
3.2 Colored by School

Now, color the vertices based on the school that each user in our network attended.

MIT
V(g)$color = "black"
V(g)$color[V(g)$school == "A"] = "red"
V(g)$color[V(g)$school == "AB"] = "gray"
plot(g, vertex.label=NA)

par(mar=c(1,1,1,1))
table(V(g)$school, useNA="ifany")

    A AB 
40 17  2 
V(g)$color = "gray"
V(g)$color[V(g)$school == "A"] = "green"
V(g)$color[V(g)$school == "AB"] = "red"
plot(g, vertex.label=NA)

Are the two users who attended both schools A and B Facebook friends with each other?

  • Yes

What best describes the users with highest degree?

  • Some, but not all, of the high-degree users attended school A
3.3 Colored by Locale

Now, color the vertices based on the locale of the user.

MIT
V(g)$color = "black"
V(g)$color[V(g)$locale == "A"] = "red"
V(g)$color[V(g)$locale == "B"] = "gray"
plot(g, vertex.label=NA)

table(V(g)$locale, useNA="ifany")

    A  B 
 3  6 50 
V(g)$color = "gray"
V(g)$color[V(g)$locale == "A"] = "red"
V(g)$color[V(g)$locale == "B"] = "green"
plot(g, vertex.label=NA)

#MIT用紅色代表A,灰色代表B,比較不清楚,老師設定的顏色處理比較明顯。

The large connected component is most associated with which locale?

  • B

The 4-user connected component is most associated with which locale?

  • A


4. Help Page for igraph Ploting

Which igraph plotting function would enable us to plot our graph in 3-D?

library(rgl)
rglplot(g, vertex.label=NA) # not working in windows

What parameter to the plot() function would we use to change the edge width when plotting g?

plot(g, edge.width=2, vertex.label=NA)








LS0tDQp0aXRsZTogIkFTNy0yIOekvuacg+e2sui3ryINCmF1dGhvcjogIum7g+afj+iejSwgTTA2NDExMTA0NiwgMjAxOC8wNy8yNSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxicj4NCg0KKirkuLvopoHorbDpoYzvvJrnpL7mnIPntrLot6/nsKHku4sqKg0KDQoqKuWtuOe/kumHjem7nu+8mioqDQoNCisg56S+5pyD57ay6Lev55qE5qeL5oiQ5YWD5Lu2DQorIOekvuacg+e2sui3r+eahOizh+aWmee1kOaniw0KKyDnpL7mnIPntrLot6/os4fmlpnnmoToppboprrljJYNCg0KDQpgYGB7ciBlY2hvPVQsIG1lc3NhZ2U9RiwgY2FjaGU9Riwgd2FybmluZz1GfQ0Kcm0obGlzdD1scyhhbGw9VCkpDQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCJDIikNCm9wdGlvbnMoZGlnaXRzPTQsIHNjaXBlbj0xMikNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkocmdsKQ0KYGBgDQo8YnI+PGhyPg0KDQojIyMgMSBTdW1tYXJpemluZyB0aGUgRGF0YQ0KDQojIyMjIyAxLjEgDQpMb2FkIHRoZSBkYXRhIGZyb20gZWRnZXMuY3N2IGludG8gYSBkYXRhIGZyYW1lIGNhbGxlZCBlZGdlcywgYW5kIGxvYWQgdGhlIGRhdGEgZnJvbSB1c2Vycy5jc3YgaW50byBhIGRhdGEgZnJhbWUgY2FsbGVkIHVzZXJzLg0KDQpgYGB7cn0NCmVkZ2VzID0gcmVhZC5jc3YoJ2RhdGEvZWRnZXMuY3N2JykNCnVzZXJzID0gcmVhZC5jc3YoJ2RhdGEvdXNlcnMuY3N2JykNCmBgYA0KDQpfSG93IG1hbnkgRmFjZWJvb2sgdXNlcnMgYXJlIHRoZXJlIGluIG91ciBkYXRhc2V0P18NCmBgYHtyfQ0KbnJvdyh1c2VycykNCmBgYA0KDQpfSW4gb3VyIGRhdGFzZXQsIHdoYXQgaXMgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGZyaWVuZHMgcGVyIHVzZXI/XyBIaW50OiB0aGlzIHF1ZXN0aW9uIGlzIHRyaWNreSwgYW5kIGl0IG1pZ2h0IGhlbHAgdG8gc3RhcnQgYnkgdGhpbmtpbmcgYWJvdXQgYSBzbWFsbCBleGFtcGxlIHdpdGggdHdvIHVzZXJzIHdobyBhcmUgZnJpZW5kcy4NCg0KIyMjIyNNSVQNCmBgYHtyfQ0Kc3RyKGVkZ2VzKSANCm5yb3coZWRnZXMpDQoxNDYqMi81OQ0KI+WboOeCukHmmK9C55qE5pyL5Y+L77yM5ZCM5pmCQuS5n+aYr0HnmoTmnIvlj4vvvIznuL3lhbHmnIkxNDblsI3nlKjmiLbmmK/lpb3lj4vvvIzmr4/kuIDlsI0oQSxCKemcgOeul+WFqeasoe+8jOWboOatpDE0NuS5mOS7pTI9Mjky77yM5YaN6Zmk5LulNTkNCmBgYCANCg0KYGBge3J9DQoyKm5yb3coZWRnZXMpL25yb3codXNlcnMpDQpgYGANCg0KIyMjIyMgMS4yDQpfT3V0IG9mIGFsbCB0aGUgc3R1ZGVudHMgd2hvIGxpc3RlZCBhIHNjaG9vbCwgd2hhdCB3YXMgdGhlIG1vc3QgY29tbW9uIGxvY2FsZT9fDQoNCiMjIyMjTUlUDQpgYGB7cn0NCnRhYmxlKHVzZXJzJGxvY2FsZSwgdXNlcnMkc2Nob29sKQ0KcHJpbnQoIkxvY2FsZSBCIikNCmBgYA0KDQoNCmBgYHtyfQ0KdGFibGUodXNlcnMkbG9jYWxlKQ0KYGBgDQoNCiMjIyMjIDEuMyANCl9JcyBpdCBwb3NzaWJsZSB0aGF0IGVpdGhlciBzY2hvb2wgQSBvciBCIGlzIGFuIGFsbC1naXJscyBvciBhbGwtYm95cyBzY2hvb2w/Xw0KYGBge3J9DQp0YWJsZShnZW5kZXI9dXNlcnMkZ2VuZGVyLCBzY2hvb2w9dXNlcnMkc2Nob29sKQ0KcHJpbnQoIk5vIikNCiPlm6DngrrlgZrlh7rkvobmgKfliKVB5ZKMQuWcqOWtuOagoUHmiJZC6YO95pyJ5YiG5biDDQpgYGANCjxicj48aHI+DQoNCiMjIyAyIENyZWF0aW5nIGEgTmV0d29yaw0KV2Ugd2lsbCBiZSB1c2luZyB0aGUgaWdyYXBoIHBhY2thZ2UgdG8gdmlzdWFsaXplIG5ldHdvcmtzOyBpbnN0YWxsIGFuZCBsb2FkIHRoaXMgcGFja2FnZSB1c2luZyB0aGUgaW5zdGFsbC5wYWNrYWdlcyBhbmQgbGlicmFyeSBjb21tYW5kcy4NCg0KIyMjIyMgMi4xIENvbnN0cnVjdCBhIEdyYXBoDQpXZSBjYW4gY3JlYXRlIGEgbmV3IGdyYXBoIG9iamVjdCB1c2luZyB0aGUgZ3JhcGguZGF0YS5mcmFtZSgpIGZ1bmN0aW9uLiBCYXNlZCBvbiBgP2dyYXBoLmRhdGEuZnJhbWVgLCANCg0KYGBge3J9DQpsaWJyYXJ5KGlncmFwaCkNCmcgPSBncmFwaC5kYXRhLmZyYW1lKGQ9ZWRnZXMsIGRpcmVjdGVkPUZBTFNFLCB2ZXJ0aWNlcz11c2VycykNCmBgYA0KX3doaWNoIG9mIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgd2lsbCBjcmVhdGUgYSBncmFwaCBnIGRlc2NyaWJpbmcgb3VyIHNvY2lhbCBuZXR3b3JrLCB3aXRoIHRoZSBhdHRyaWJ1dGVzIG9mIGVhY2ggdXNlciBjb3JyZWN0bHkgbG9hZGVkP18NCg0KKyBgZyA9IGdyYXBoLmRhdGEuZnJhbWUoZWRnZXMsIEZBTFNFLCB1c2VycylgDQorDQoNCk5vdGU6IEEgZGlyZWN0ZWQgZ3JhcGggaXMgb25lIHdoZXJlIHRoZSBlZGdlcyBvbmx5IGdvIG9uZSB3YXkgLS0gdGhleSBwb2ludCBmcm9tIG9uZSB2ZXJ0ZXggdG8gYW5vdGhlci4gVGhlIG90aGVyIG9wdGlvbiBpcyBhbiB1bmRpcmVjdGVkIGdyYXBoLCB3aGljaCBtZWFucyB0aGF0IHRoZSByZWxhdGlvbnMgYmV0d2VlbiB0aGUgdmVydGljZXMgYXJlIHN5bW1ldHJpYy4NCg0KIyMjIyMgMi4yIENvbXBvbmVudHMNClVzZSB0aGUgY29ycmVjdCBjb21tYW5kIGZyb20gUHJvYmxlbSAyLjEgdG8gbG9hZCB0aGUgZ3JhcGggZy4NCg0KTm93LCB3ZSB3YW50IHRvIHBsb3Qgb3VyIGdyYXBoLiBCeSBkZWZhdWx0LCB0aGUgdmVydGljZXMgYXJlIGxhcmdlIGFuZCBoYXZlIHRleHQgbGFiZWxzIG9mIGEgdXNlcidzIGlkZW50aWZpZXIuIEJlY2F1c2UgdGhpcyB3b3VsZCBjbHV0dGVyIHRoZSBvdXRwdXQsIHdlIHdpbGwgcGxvdCB3aXRoIG5vIHRleHQgbGFiZWxzIGFuZCBzbWFsbGVyIHZlcnRpY2VzOg0KYGBge3J9DQpwbG90KGcsIHZlcnRleC5zaXplPTUsIHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KDQpJbiB0aGlzIGdyYXBoLCB0aGVyZSBhcmUgYSBudW1iZXIgb2YgZ3JvdXBzIG9mIG5vZGVzIHdoZXJlIGFsbCB0aGUgbm9kZXMgaW4gZWFjaCBncm91cCBhcmUgY29ubmVjdGVkIGJ1dCB0aGUgZ3JvdXBzIGFyZSBkaXNqb2ludCBmcm9tIG9uZSBhbm90aGVyLCBmb3JtaW5nICJpc2xhbmRzIiBpbiB0aGUgZ3JhcGguIFN1Y2ggZ3JvdXBzIGFyZSBjYWxsZWQgImNvbm5lY3RlZCBjb21wb25lbnRzLCIgb3IgImNvbXBvbmVudHMiIGZvciBzaG9ydC4gX0hvdyBtYW55IGNvbm5lY3RlZCBjb21wb25lbnRzIHdpdGggYXQgbGVhc3QgMiBub2RlcyBhcmUgdGhlcmUgaW4gdGhlIGdyYXBoP18NCg0KKyA0DQorIA0KDQpfSG93IG1hbnkgdXNlcnMgYXJlIHRoZXJlIHdpdGggbm8gZnJpZW5kcyBpbiB0aGUgbmV0d29yaz9fDQorIDcNCisgDQoNCiMjIyMjIDIuMyBEZWdyZWUgDQpJbiBvdXIgZ3JhcGgsIHRoZSAiZGVncmVlIiBvZiBhIG5vZGUgaXMgaXRzIG51bWJlciBvZiBmcmllbmRzLiBXZSBoYXZlIGFscmVhZHkgc2VlbiB0aGF0IHNvbWUgbm9kZXMgaW4gb3VyIGdyYXBoIGhhdmUgZGVncmVlIDAgKHRoZXNlIGFyZSB0aGUgbm9kZXMgd2l0aCBubyBmcmllbmRzKSwgd2hpbGUgb3RoZXJzIGhhdmUgbXVjaCBoaWdoZXIgZGVncmVlLiBXZSBjYW4gdXNlIGRlZ3JlZShnKSB0byBjb21wdXRlIHRoZSBkZWdyZWUgb2YgYWxsIHRoZSBub2RlcyBpbiBvdXIgZ3JhcGggZy4NCg0KIyMjIyNNSVQNCmBgYHtyfQ0KdGFibGUoZGVncmVlKGcpKQ0KdGFibGUoZGVncmVlKGcpID49IDEwKQ0KIzEw5YCL5Lul5LiK55qE55So5oi2OjQrMSsyKzErMT05DQpgYGANCg0KDQpfSG93IG1hbnkgdXNlcnMgYXJlIGZyaWVuZHMgd2l0aCAxMCBvciBtb3JlIG90aGVyIEZhY2Vib29rIHVzZXJzIGluIHRoaXMgbmV0d29yaz9fDQpgYGB7cn0NCnN1bShkZWdyZWUoZykgPj0gMTApDQpgYGANCg0KIyMjIyMgMi40IFNpemUgYnkgRGVncmVlDQpJbiBhIG5ldHdvcmssIGl0J3Mgb2Z0ZW4gdmlzdWFsbHkgdXNlZnVsIHRvIGRyYXcgYXR0ZW50aW9uIHRvICJpbXBvcnRhbnQiIG5vZGVzIGluIHRoZSBuZXR3b3JrLiBXaGlsZSB0aGlzIG1pZ2h0IG1lYW4gZGlmZmVyZW50IHRoaW5ncyBpbiBkaWZmZXJlbnQgY29udGV4dHMsIGluIGEgc29jaWFsIG5ldHdvcmsgd2UgbWlnaHQgY29uc2lkZXIgYSB1c2VyIHdpdGggYSBsYXJnZSBudW1iZXIgb2YgZnJpZW5kcyB0byBiZSBhbiBpbXBvcnRhbnQgdXNlci4gRnJvbSB0aGUgcHJldmlvdXMgcHJvYmxlbSwgd2Uga25vdyB0aGlzIGlzIHRoZSBzYW1lIGFzIHNheWluZyB0aGF0IG5vZGVzIHdpdGggYSBoaWdoIGRlZ3JlZSBhcmUgaW1wb3J0YW50IHVzZXJzLg0KDQpUbyB2aXN1YWxseSBkcmF3IGF0dGVudGlvbiB0byB0aGVzZSBub2Rlcywgd2Ugd2lsbCBjaGFuZ2UgdGhlIHNpemUgb2YgdGhlIHZlcnRpY2VzIHNvIHRoZSB2ZXJ0aWNlcyB3aXRoIGhpZ2ggZGVncmVlcyBhcmUgbGFyZ2VyLiBUbyBkbyB0aGlzLCB3ZSB3aWxsIGNoYW5nZSB0aGUgInNpemUiIGF0dHJpYnV0ZSBvZiB0aGUgdmVydGljZXMgb2Ygb3VyIGdyYXBoIHRvIGJlIGFuIGluY3JlYXNpbmcgZnVuY3Rpb24gb2YgdGhlaXIgZGVncmVlczoNCmBgYHtyfQ0KVihnKSRzaXplID0gZGVncmVlKGcpLzIrMiANCnBsb3QoZywgdmVydGV4LmxhYmVsPU5BKQ0KI+aKiuacieWkp+mHj+iHieabuOaci+WPi+eahOmggum7nuaUvuWkpw0KYGBgDQoNCl9XaGF0IGlzIHRoZSBsYXJnZXN0IHNpemUgd2UgYXNzaWduZWQgdG8gYW55IG5vZGUgaW4gb3VyIGdyYXBoP18gPGJyPg0KX1doYXQgaXMgdGhlIHNtYWxsZXN0IHNpemUgd2UgYXNzaWduZWQgdG8gYW55IG5vZGUgaW4gb3VyIGdyYXBoP18NCg0KIyMjIyNNSVQNCmBgYHtyfQ0KdGFibGUoZGVncmVlKGcpKQ0Kc3VtbWFyeShkZWdyZWUoZykpDQpgYGANCg0KDQpgYGB7cn0NCnJhbmdlKFYoZykkc2l6ZSkNCmBgYA0KPGJyPjxocj4NCg0KIyMjIDMgQ29sb3JpbmcgVmVydGljZXMNCg0KIyMjIyMgMy4xIENvbG9yZWQgYnkgR2VuZGVyDQpUaHVzIGZhciwgd2UgaGF2ZSBjaGFuZ2VkIHRoZSAic2l6ZSIgYXR0cmlidXRlcyBvZiBvdXIgdmVydGljZXMuIEhvd2V2ZXIsIHdlIGNhbiBhbHNvIGNoYW5nZSB0aGUgY29sb3JzIG9mIHZlcnRpY2VzIHRvIGNhcHR1cmUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgRmFjZWJvb2sgdXNlcnMgd2UgYXJlIGRlcGljdGluZy4NCg0KV2hlbiBjaGFuZ2luZyB0aGUgc2l6ZSBvZiBub2Rlcywgd2UgZmlyc3Qgb2J0YWluZWQgdGhlIHZlcnRpY2VzIG9mIG91ciBncmFwaCB3aXRoIGBWKGcpYCBhbmQgdGhlbiBhY2Nlc3NlZCB0aGUgdGhlIHNpemUgYXR0cmlidXRlIHdpdGggVihnKSRzaXplLiBUbyBjaGFuZ2UgdGhlIGNvbG9yLCB3ZSB3aWxsIHVwZGF0ZSB0aGUgYXR0cmlidXRlIGBWKGcpJGNvbG9yYC4NCg0KVG8gY29sb3IgdGhlIHZlcnRpY2VzIGJhc2VkIG9uIHRoZSBnZW5kZXIgb2YgdGhlIHVzZXIsIHdlIHdpbGwgbmVlZCBhY2Nlc3MgdG8gdGhhdCB2YXJpYWJsZS4gV2hlbiB3ZSBjcmVhdGVkIG91ciBncmFwaCBnLCB3ZSBwcm92aWRlZCBpdCB3aXRoIHRoZSBkYXRhIGZyYW1lIHVzZXJzLCB3aGljaCBoYWQgdmFyaWFibGVzIGdlbmRlciwgc2Nob29sLCBhbmQgbG9jYWxlLiBUaGVzZSBhcmUgbm93IHN0b3JlZCBhcyBhdHRyaWJ1dGVzIFYoZykkZ2VuZGVyLCBWKGcpJHNjaG9vbCwgYW5kIFYoZykkbG9jYWxlLg0KDQpXZSBjYW4gdXBkYXRlIHRoZSBjb2xvcnMgYnkgc2V0dGluZyB0aGUgY29sb3IgdG8gYmxhY2sgZm9yIGFsbCB2ZXJ0aWNlcywgdGhhbiBzZXR0aW5nIGl0IHRvIHJlZCBmb3IgdGhlIHZlcnRpY2VzIHdpdGggZ2VuZGVyIEEgYW5kIHNldHRpbmcgaXQgdG8gZ3JheSBmb3IgdGhlIHZlcnRpY2VzIHdpdGggZ2VuZGVyIEI6DQoNClBsb3QgdGhlIHJlc3VsdGluZyBncmFwaC4gDQpgYGB7cn0NClYoZykkY29sb3IgPSAiYmxhY2siDQpWKGcpJGNvbG9yW1YoZykkZ2VuZGVyID09ICJBIl0gPSAicmVkIg0KVihnKSRjb2xvcltWKGcpJGdlbmRlciA9PSAiQiJdID0gImdyYXkiDQpwbG90KGcsIHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KX1doYXQgaXMgdGhlIGdlbmRlciBvZiB0aGUgdXNlcnMgd2l0aCB0aGUgaGlnaGVzdCBkZWdyZWUgaW4gdGhlIGdyYXBoP18NCg0KKyBCDQorDQoNCiMjIyMjIDMuMiBDb2xvcmVkIGJ5IFNjaG9vbA0KTm93LCBjb2xvciB0aGUgdmVydGljZXMgYmFzZWQgb24gdGhlIHNjaG9vbCB0aGF0IGVhY2ggdXNlciBpbiBvdXIgbmV0d29yayBhdHRlbmRlZC4NCg0KIyMjIyNNSVQNCmBgYHtyfQ0KVihnKSRjb2xvciA9ICJibGFjayINClYoZykkY29sb3JbVihnKSRzY2hvb2wgPT0gIkEiXSA9ICJyZWQiDQpWKGcpJGNvbG9yW1YoZykkc2Nob29sID09ICJBQiJdID0gImdyYXkiDQpwbG90KGcsIHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KDQoNCmBgYHtyfQ0KcGFyKG1hcj1jKDEsMSwxLDEpKQ0KdGFibGUoVihnKSRzY2hvb2wsIHVzZU5BPSJpZmFueSIpDQpgYGANCg0KYGBge3IgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NiwgZmlnLmFsaWduPSJjZW50ZXIifQ0KVihnKSRjb2xvciA9ICJncmF5Ig0KVihnKSRjb2xvcltWKGcpJHNjaG9vbCA9PSAiQSJdID0gImdyZWVuIg0KVihnKSRjb2xvcltWKGcpJHNjaG9vbCA9PSAiQUIiXSA9ICJyZWQiDQpwbG90KGcsIHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KX0FyZSB0aGUgdHdvIHVzZXJzIHdobyBhdHRlbmRlZCBib3RoIHNjaG9vbHMgQSBhbmQgQiBGYWNlYm9vayBmcmllbmRzIHdpdGggZWFjaCBvdGhlcj9fDQoNCisgWWVzDQorDQoNCl9XaGF0IGJlc3QgZGVzY3JpYmVzIHRoZSB1c2VycyB3aXRoIGhpZ2hlc3QgZGVncmVlP18gDQoNCisgU29tZSwgYnV0IG5vdCBhbGwsIG9mIHRoZSBoaWdoLWRlZ3JlZSB1c2VycyBhdHRlbmRlZCBzY2hvb2wgQQ0KKw0KDQoNCiMjIyMjIDMuMyBDb2xvcmVkIGJ5IExvY2FsZQ0KTm93LCBjb2xvciB0aGUgdmVydGljZXMgYmFzZWQgb24gdGhlIGxvY2FsZSBvZiB0aGUgdXNlci4NCg0KIyMjIyNNSVQNCmBgYHtyfQ0KVihnKSRjb2xvciA9ICJibGFjayINClYoZykkY29sb3JbVihnKSRsb2NhbGUgPT0gIkEiXSA9ICJyZWQiDQpWKGcpJGNvbG9yW1YoZykkbG9jYWxlID09ICJCIl0gPSAiZ3JheSINCnBsb3QoZywgdmVydGV4LmxhYmVsPU5BKQ0KYGBgDQoNCg0KYGBge3J9DQp0YWJsZShWKGcpJGxvY2FsZSwgdXNlTkE9ImlmYW55IikNCmBgYA0KDQpgYGB7ciBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02LCBmaWcuYWxpZ249ImNlbnRlciJ9DQpWKGcpJGNvbG9yID0gImdyYXkiDQpWKGcpJGNvbG9yW1YoZykkbG9jYWxlID09ICJBIl0gPSAicmVkIg0KVihnKSRjb2xvcltWKGcpJGxvY2FsZSA9PSAiQiJdID0gImdyZWVuIg0KcGxvdChnLCB2ZXJ0ZXgubGFiZWw9TkEpDQojTUlU55So57SF6Imy5Luj6KGoQe+8jOeBsOiJsuS7o+ihqELvvIzmr5TovIPkuI3muIXmpZrvvIzogIHluKvoqK3lrprnmoTpoY/oibLomZXnkIbmr5TovIPmmI7poa/jgIINCmBgYA0KDQpfVGhlIGxhcmdlIGNvbm5lY3RlZCBjb21wb25lbnQgaXMgbW9zdCBhc3NvY2lhdGVkIHdpdGggd2hpY2ggbG9jYWxlP18NCg0KKyBCDQorIA0KDQpfVGhlIDQtdXNlciBjb25uZWN0ZWQgY29tcG9uZW50IGlzIG1vc3QgYXNzb2NpYXRlZCB3aXRoIHdoaWNoIGxvY2FsZT9fDQoNCisgQQ0KKyANCg0KPGJyPjxocj4NCg0KIyMjIDQuIEhlbHAgUGFnZSBmb3IgYGlncmFwaGAgUGxvdGluZyANCg0KX1doaWNoIGlncmFwaCBwbG90dGluZyBmdW5jdGlvbiB3b3VsZCBlbmFibGUgdXMgdG8gcGxvdCBvdXIgZ3JhcGggaW4gMy1EP18NCmBgYHtyfQ0KbGlicmFyeShyZ2wpDQpyZ2xwbG90KGcsIHZlcnRleC5sYWJlbD1OQSkgIyBub3Qgd29ya2luZyBpbiB3aW5kb3dzDQpgYGANCg0KX1doYXQgcGFyYW1ldGVyIHRvIHRoZSBwbG90KCkgZnVuY3Rpb24gd291bGQgd2UgdXNlIHRvIGNoYW5nZSB0aGUgZWRnZSB3aWR0aCB3aGVuIHBsb3R0aW5nIGBnYD9fDQpgYGB7cn0NCnBsb3QoZywgZWRnZS53aWR0aD0yLCB2ZXJ0ZXgubGFiZWw9TkEpDQpgYGANCjxicj48aHI+DQoNCg0KPGJyPjxicj48YnI+PGJyPjxicj4NCg0KPHN0eWxlPg0KLmNhcHRpb24gew0KICBjb2xvcjogIzc3NzsNCiAgbWFyZ2luLXRvcDogMTBweDsNCn0NCnAgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcHJlIHsNCiAgd29yZC1icmVhazogbm9ybWFsOw0KICB3b3JkLXdyYXA6IG5vcm1hbDsNCiAgbGluZS1oZWlnaHQ6IDE7DQp9DQpwcmUgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcCxsaSB7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQoucnsNCiAgbGluZS1oZWlnaHQ6IDEuMjsNCn0NCg0KdGl0bGV7DQogIGNvbG9yOiAjY2MwMDAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KYm9keXsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgxLGgyLGgzLGg0LGg1ew0KICBjb2xvcjogIzAwODgwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgzew0KICBjb2xvcjogI2IzNmIwMDsNCiAgYmFja2dyb3VuZDogI2ZmZTBiMzsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoNXsNCiAgY29sb3I6ICMwMDYwMDA7DQogIGJhY2tncm91bmQ6ICNmZmZmZTA7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KZW17DQogIGNvbG9yOiAjMDAwMGMwOw0KICBiYWNrZ3JvdW5kOiAjZjBmMGYwOw0KICB9DQo8L3N0eWxlPg0KDQo=