Social Network Analysis
Basic counting
Next we will do some more descriptive analysis.
How many nodes are in the network?
vcount(g_bimodal_twitter)
[1] 100
How many edges in the network?
ecount(g_bimodal_twitter)
[1] 160
Get a list of the nodes in the network:
V(g_bimodal_twitter)
+ 100/100 vertices, named, from 9009817:
[1] rainey_knight Warstub Charlie5009 ballgameskeith msrose2343 trollsforpeace atheist4maga ochreblue
[9] JoRobinson_Aus PreAmpPlus croswell_g bprophetable plabg GoldSuzie asciigoat Kaneosaurus
[17] BobOfAlex Firemonkey991 NaomiCra TotalREProperty dannytweeets 4U_WTF detispify allisonBeDemure
[25] Bev_n_W Medicayy indica2007 KirstyWho RrRjrobinson9 Redbank80Graeme stopcoalexports InvestInScience
[33] Left_of_Labor AliKayaBrisbane AngelaKorras PilligaPush FeeFeeCee amagickeagle999 MrNixonsWife MalurusSally
[41] SocialistMason Salig08 idiosoCamel GoldCoastNurse Broadband_ madnyc Elaine_de_Saxe johngwass
[49] cleverclicks MsJmaid MiztaRabbit mishyloan sierra4oz TaodeHaas Ducatio_ #auspol
[57] #nrlgf #ausunions #QandA #AusPol #Islam #Paris #GrandMufti #Auspol
[65] #Adani #StopAdani #MarriageEquality #ReachTEL #Manus #Nauru #4Corners #CSG
[73] #qldpol #India #Queensland #coal #adani #VoteYes #climatechange #Trump
+ ... omitted several vertices
List of edges in the network:
E(g_bimodal_twitter)
+ 160/160 edges from 9009817 (vertex names):
[1] rainey_knight ->#auspol rainey_knight ->#nrlgf Warstub ->#auspol Warstub ->#ausunions
[5] Charlie5009 ->#auspol ballgameskeith->#QandA ballgameskeith->#ausunions ballgameskeith->#auspol
[9] msrose2343 ->#auspol trollsforpeace->#AusPol atheist4maga ->#Islam atheist4maga ->#Paris
[13] atheist4maga ->#GrandMufti atheist4maga ->#Auspol ochreblue ->#Adani ochreblue ->#StopAdani
[17] ochreblue ->#auspol JoRobinson_Aus->#auspol JoRobinson_Aus->#MarriageEquality PreAmpPlus ->#ReachTEL
[21] PreAmpPlus ->#auspol croswell_g ->#Islam croswell_g ->#Paris croswell_g ->#GrandMufti
[25] croswell_g ->#Auspol ochreblue ->#StopAdani ochreblue ->#auspol bprophetable ->#Manus
[29] bprophetable ->#Nauru bprophetable ->#auspol plabg ->#auspol ochreblue ->#StopAdani
[33] ochreblue ->#auspol ochreblue ->#4Corners ochreblue ->#StopAdani ochreblue ->#auspol
[37] GoldSuzie ->#auspol GoldSuzie ->#CSG ochreblue ->#4Corners ochreblue ->#StopAdani
+ ... omitted several edges
Access a particular node in the network (node #42):
V(g_bimodal_twitter)[42]
+ 1/100 vertex, named, from 9009817:
[1] Salig08
Access a particular edges:
E(g_bimodal_twitter)[1]
+ 1/160 edge from 9009817 (vertex names):
[1] rainey_knight->#auspol
Graph connectivity
Look at the connectivity of the graph:
# who are the neighbours of node #42?
neighbors(g_bimodal_facebook_star_wars,42)
+ 1/1208 vertex, named, from 8e21e39:
[1] 169299103121699_909270052457930
#this is not a weakly connected component
is.connected(g_bimodal_facebook_star_wars, mode="weak")
[1] TRUE
#information on connected components
cc <- clusters(g_bimodal_facebook_star_wars)
#which component node is assigned to
# cc$membership
#size of each component
cc$csize
[1] 1208
#number of components
cc$no
[1] 1
#subnetwork - giant component
g3 <- induced_subgraph(g_bimodal_facebook_star_wars, which(cc$membership == which.max(cc$csize)))
We will now look at node centrality:
#node indegree
degree(g3, mode="in")
#node outdegree
degree(g3, mode="out")
#node indegree, using edge weights
ind <- strength(g3, mode="in")
#top-5 nodes, based on (weighted) indegree
V(g3)[order(ind, decreasing=T)[1:3]]
#closeness centrality
closeness(g3)
#betweenness centrality
betweenness(g3)
#eigenvector centrality
evcent(g3)$vector
We can look at some network cohesion measures. How dense is the graph? In other words, of all the possible connections between nodes, how many are actually observed?
# density
graph.density(g3)
[1] 0.0008367306
# (global) clustering coefficient
# rel. frequency connected triples close to form triangles
transitivity(g3)
[1] 0
# number of dyads with reciprocated (mutual)
# edges/number of dyads with single edge
reciprocity(g3, mode="default")
[1] 0
#total number of reciprocated edges/total number of edges
reciprocity(g3, mode="ratio")
[1] 0
Find important nodes in the network
Who are the top 3 most important posts in the Facebook network? There are several ways to do this. For fun we will use the PageRank algorithm implementation in igraph to calculate this. PageRank is made famous by the Google co-founders, who invented this method to determine the importance of webpages, revolutionising the search engine industry. The following code calculates PageRank for nodes in the network, and returns the 3 ‘top’ nodes (which have the highest share of PageRank), providing the ID.
pagerank_instagram <- sort(page.rank(g_bimodal_facebook_star_wars)$vector,decreasing=TRUE)
head(pagerank_instagram,n=3)
169299103121699_909270052457930 AL Rbg Abraham Gonzales
0.4597014257 0.0004476376 0.0004476376
What are the top 10 important terms in our #auspol actor network? There is no reason why we can’t use the PageRank algorithm to calculate this (as per the Instagram analysis previously):
pageRank_auspol_semantic <- sort(page.rank(g_twitter_semantic)$vector,decreasing=TRUE)
head(pageRank_auspol_semantic,n=10)
#auspol auspol #csg adani #adani qldpol #stopadani #4corners stopadani #qldpol
0.18150152 0.15599852 0.08531406 0.03674505 0.02625074 0.02496031 0.02491640 0.02386476 0.02338559 0.02305313
What about the 3 least important users (with all due respect…):
tail(pageRank_auspol_semantic,n=3)
#lockthegate #northkorea #reachtel
0.00360652 0.00360652 0.00360652
Obviously the #auspol hashtag is going to be the most important because it occurs at least once in every tweet. We can actually avoid this by using the removeTermsOrHashtags argument when we Create() the network. This argument specifies which terms or hashtags (i.e. nodes with a name that matches one or more terms) should be removed from the semantic network. This is useful to remove the search term or hashtag that was used to collect the data (i.e. remove the corresponding node in the graph). For example, a value of “#auspol” means that the node with the name “#auspol” will be removed. Note: you could also just delete the #auspol node manually.
Another key aspect of semantic networks is how many terms to include in the network. By default, SocialMediaLab does not include every unique term that it finds in the tweets, but only the 5 percent most frequently occurring terms. You can change this when calling the Create() network function, for example by specifying a value 50 (meaning that the 50 percent most frequently occurring terms will be included in the semantic network).
We can actually try this out now. We will create another semantic network, but we will exclude the #auspol hashtag, and we will include every single term available in the tweets.
g_twitter_semantic_auspol_allTerms <- myTwitterData %>% Create("Semantic", termFreq=100, removeTermsOrHashtags=c("#auspol"))
[1] "Generating Twitter semantic network..."
Done.
The size of the network will increase a lot, even in the absence of the #auspol term!
g_twitter_semantic_auspol_allTerms
IGRAPH db51550 UNW- 385 663 --
+ attr: name (v/c), label (v/c), weight (e/n)
+ edges from db51550 (vertex names):
[1] auspol --#nrlgf turnbullmalcolm--#nrlgf great --#nrlgf nrlgf --#nrlgf turnbull --#nrlgf
[6] johnwren --#nrlgf judgement --#nrlgf keating --#nrlgf said --#nrlgf men --#nrlgf
[11] auspol --#ausunions ausunions --#ausunions cut --#ausunions pay --#ausunions boycott --#ausunions
[16] cream --#ausunions ice --#ausunions streets --#ausunions unionsaustralia--#ausunions workers --#ausunions
[21] australia --#ausunions auspol --#qanda ausunions --#qanda #qanda --qanda #qanda --taken
[26] #qanda --clubegaffer #qanda --hasnt #qanda --increase #qanda --job #qanda --profiteer
[31] #qanda --robot #qanda --wealth #qanda --take #ausunions --qanda #ausunions --taken
[36] #ausunions --clubegaffer #ausunions --hasnt #ausunions --increase #ausunions --job #ausunions --profiteer
+ ... omitted several edges
What are the top 10 important terms in our semantic network now? Once again we will calculate this using PageRank:
pageRank_auspol_semantic_replicate <- sort(page.rank(g_twitter_semantic_auspol_allTerms)$vector,decreasing=TRUE)
pageRank_auspol_semantic_replicate[1:10]
#4corners #adani auspol #csg #qldpol #stopadani #voteyes #qanda #ausunions #nrlgf
0.03945061 0.03310908 0.03280464 0.03179612 0.03114190 0.03057364 0.02554519 0.02432106 0.02142076 0.01754020
Find communities
Is there any kind of community structure within the user network? We will use the infomap algorithm implementation in igraph.
library(igraph)
# increase nb.trials for better quality communities
imc <- infomap.community(g_twitter_actor, nb.trials = 10)
Modularity is implemented for undirected graphs only.
# create a vector of users with their assigned community number
communityMembership_auspol <- membership(imc)
# summarise the distribution of users to communities
commDistribution <- summary(as.factor(communityMembership_auspol))
# which community has the max number of users
tail(sort(commDistribution), n = 1)
1
81
Look into the members of each community:
# create a list of communities that includes the users assigned to each community
communities_auspol <- communities(imc)
# look at the members of the most populated community
communities_auspol[names(tail(sort(commDistribution),n=1))]
$`1`
[1] "Warstub" "ochreblue" "PreAmpPlus" "plabg" "BobOfAlex" "Firemonkey991" "NaomiCra" "FeeFeeCee"
[9] "SocialistMason" "Elaine_de_Saxe" "mishyloan" "Ducatio_" "jurylady5" "MSMWatchdog2013" "Isayneversaydie" "pleaseuseaussie"
[17] "HittingAlice" "MischelleCamill" "1JoyDuck" "Ruxyrob" "rustenburg_J" "defendressofsan" "MdmAbsentMinded" "Left_of_Labor"
[25] "Angelioannou" "AnnalieseRoss" "OzMacca46" "pbro2333_brown" "r7yrb7" "blanketcrap" "mormorlady" "VeriteGrace"
[33] "AmbientUXr" "JayjaysMd" "tilleyfab" "alicia94985048" "oldjoeschmo" "energy_bu" "vonoviedo" "drewsmilitia"
[41] "HOSKINMANDY22" "MilesChamp" "mavisgrizzltits" "deniseshrivell" "leafyflower1" "JustDoingJunk" "Eschertology" "wsj2150"
[49] "KATHS97" "ceciliemurray" "bigislandwa" "IndiBlu" "KCGMSuperpit" "sacarlin48" "cameron_gobbo1" "ajcdjp"
[57] "fredanurks" "Ned_Kelly" "unionsaustralia" "GhostWhoVotes" "lovethatloaf" "hearyanow" "CFMEUJohnSetka" "WendyFarmer_"
[65] "MGliksmanMDPhD" "LesStonehouse" NA "AustralianLabor" "jackietrad" "OzSheela" "SaveOurSpit" "GetUp"
[73] "43a6f0ce5dac4ea" "gautam_adani" "AdaniOnline" "JulieBishopMP" "4corners" "JoshFrydenberg" "FergusonNews" "neighbour_s"
[81] "StephenLongAus"
Graph Projection
Another useful technique we can do is to perform a projection of the Facebook networks we just created. These networks are bipartite because nodes of the same type cannot share an edge (e.g. a user can only like/comment on a post, but not like/comment another user, and posts cannot perform directed actions either on users or other posts).
What we can do is induce two subgraphs from each network. More specifically, we can induce two actor networks, one for the users and one for the posts.
## some data preparation
# coerce to factor
g_bimodal_facebook_star_trek_projection <- g_bimodal_facebook_star_trek
V(g_bimodal_facebook_star_trek_projection)$type <- as.factor(V(g_bimodal_facebook_star_trek_projection)$type)
# coerce all posts (i.e. "1") to logical (i.e. FALSE)
V(g_bimodal_facebook_star_trek_projection)$type[which(V(g_bimodal_facebook_star_trek_projection)$type=="1")] <- as.logical(FALSE)
# coerce all users (i.e. "2") to logical (i.e. TRUE)
V(g_bimodal_facebook_star_trek_projection)$type[which(V(g_bimodal_facebook_star_trek_projection)$type=="2")] <- as.logical(TRUE)
# now project the network
projection_g_bimodal_facebook_star_trek <- bipartite.projection(g_bimodal_facebook_star_trek_projection)
vertex types converted to logical
Firstly, we will look at the induced graph for the “posts”. The induced “posts”" actor network consists only of nodes that are of type “post”. An edge exists between post i and post j if they are both co-liked or co-commented by the same user (i.e. if they have any user in common). Not surprisingly, every post has at least one user in common, which results in the network being “complete”.
projection_g_bimodal_facebook_star_trek[[1]]
IGRAPH b541d3a UN-- 1 0 --
+ attr: name (v/c), label (v/c)
+ edges from b541d3a (vertex names):
# png('facebook_star_trek_posts.png', width=800, height=700)
plot(projection_g_bimodal_facebook_star_trek[[1]], edge.width = 1.5, edge.curved = 0.5,
edge.arrow.size = 0.5) #vertex.shape='none',

# dev.off()
Secondly, we will look at the induced graph for the “users”. The induced “users” actor network consists only of nodes that are of type “user”. An edge exists between user i and user j if they both co-liked or co-commented the same post (i.e. they share an interaction with a post j). As you might expect, this create a network with a massive number of edges! A lot of users co-interact with the same posts. For this example, over 4.5 million edges (your results might be somewhat different).
# warning - do not use ‘str‘ function because it will
# cause R to freeze up due to overloading the console output!
# Also: you will probably have difficulty plotting this graph in R because it is so big
projection_g_bimodal_facebook_star_trek[[2]]
IGRAPH 5aa91b1 UNW- 1155 666435 --
+ attr: name (v/c), label (v/c), weight (e/n)
+ edges from 5aa91b1 (vertex names):
[1] A Louise Garber--A.J. Zeien A Louise Garber--Aaron Carroll A Louise Garber--Aaron Niedzielski
[4] A Louise Garber--Adalmir Quintanilha A Louise Garber--Adam Manny Breaux A Louise Garber--Adam Redfern
[7] A Louise Garber--Adam Terrazas A Louise Garber--Addie Tennant A Louise Garber--Aditya Singh
[10] A Louise Garber--Aditya Tamhankar A Louise Garber--Adriana McGee A Louise Garber--Akira Kudou
[13] A Louise Garber--Al Shakespeare A Louise Garber--Al Stikeleather A Louise Garber--Alain Adriaenssens
[16] A Louise Garber--Alan C. Huffines A Louise Garber--Alan Crumb A Louise Garber--Alan George
[19] A Louise Garber--Albert Orkenbjorken A Louise Garber--Alberto Gudi<U+00F1>o A Louise Garber--Aldemar L<U+00F3>pez
[22] A Louise Garber--Ale Pescus A Louise Garber--Alejandro Santillan A Louise Garber--Alex Cheatom
+ ... omitted several edges
Maybe there is some community structure to this large network. There are several ways to find out. We will use the infomap algorithm implementation in igraph. Infomap uses an information theoretic, flow-based approach to calculating community structure in networks. It supports weighted and directed graphs, and also scales well.
The results show that there is definitely some interesting community structure to the user actor network (a handful of large communities and a tiny community). Although your results might di er, depending on the actual data collected.
# limit the <U+2018>trials<U+2018> argument to a small number to save time
# (number of attempts to partition the network)
imc_starwars <- infomap.community(projection_g_bimodal_facebook_star_trek[[2]],
nb.trials = 3)
# create a vector of users with their assigned community number
communityMembership_starwars <- membership(imc_starwars)
# summarise the distribution of users to communities
commDistribution_starwars <- summary(as.factor(communityMembership_starwars))
# which community has the max number of users
tail(sort(commDistribution_starwars), n = 1)
# create a list of communities that includes the users assigned to each
# community
communities_starwars <- communities(imc_starwars)
# look at the members of the *least* populated community
communities_starwars[names(head(sort(commDistribution_starwars), n = 1))]
Text Analysis
Next, we will do some descriptive text analysis of the Star Wars fan comments.
TODO
Data pre-processing
We just want to keep the character vector of ‘comments’ data, for our purposes in this session:
fbData <- myStarWarsData$commentText
We only want elements of fbData that contain comment text (many rows of our Facebook data represent ‘likes’, rather than ‘comments’). So we remove any text data that equals “Not_applicable” (this is how SocialMediaLab designates rows in the dataframe that are ‘likes’). Note: in earlier versions of SocialMediaLab these elements were designated as NA, however this caused unintended consequences so it was changed.
toRemove <- which(fbData=="Not_applicable")
fbData <- fbData[-toRemove] # remove the elements we want to exclude
How many comments do we have left now?
length(fbData)
[1] 220
We convert the character encoding to UTF-8. This avoids errors relating to ‘odd’ characters in the text. This is usually a good idea, but there may be situations when it is not useful, or even detrimental. Note: Mac users may encounter errors/bugs relating to character encoding, and a workaround is to convert to ‘utf-8-mac’:
fbData <- iconv(fbData, to = "utf-8")
# **MAC USERS ONLY** should use this instead:
fbData <- iconv(fbData,to="utf-8-mac")
We convert our character vector fbData to a Vcorpus object:
library(tm)
fbCorpus <- VCorpus(VectorSource(fbData))
Individual comments (a.k.a. ‘documents’) can be accessed via the double brackets notation or the ‘dollar sign’ notation for accessing list elements. Let’s look at comment #4.
fbCorpus[[4]][[1]]
[1] "OMG"
# another way to access it
fbCorpus[[4]]$content
[1] "OMG"
We can perform a number of highly useful transformations of text using tm_map function (i.e. ‘mapping to the corpus’). Not all of these transformations are useful in every scenario! They should be used only when it makes sense, or as required, etc.
Converting all the text to lowercase:
fbCorpus <- tm_map(fbCorpus, content_transformer(tolower))
Remove numbers from the text:
fbCorpus <- tm_map(fbCorpus, removeNumbers)
Remove punctuation from the text:
fbCorpus <- tm_map(fbCorpus, removePunctuation)
Perform ‘word stemming’ on the text. Note: this transformation can be highly useful, but also highly detrimental!
# use lazy=TRUE argument to avoid warning on some machines with multiple CPU cores
fbCorpus <- tm_map(fbCorpus, stemDocument,lazy=TRUE)
We can also remove English ‘stop words’ from the text. These are common words (e.g. ‘the’, ‘and’, ‘or’) that we may want to exclude from our analysis. Once again, this is highly useful but also needs to be carefully applied.
fbCorpus <- tm_map(fbCorpus, removeWords, stopwords("english"),lazy=TRUE)
# use lazy=TRUE argument to avoid warning on some machines with multiple CPU cores
Eliminate unnecessary ‘white space’ from the text. For example, “hello everyone my name is fred” becomes “hello everyone my name is fred”:
fbCorpus <- tm_map(fbCorpus, stripWhitespace, lazy=TRUE)
We can observe the di?erence now by examining comment #4 again:
fbCorpus[[4]]$content
[1] "omg"
We could also define our own stop words and transform the text using these:
myStopwords <- c("jar","binks")
fbCorpus <- tm_map(fbCorpus, removeWords, myStopwords)
Frequency analysis
Next we create a document-term matrix (DTM) from the fbCorpus object. DTMs are a very important concept for text analysis and are highly useful. DTMs can be thought about as a table (i.e. matrix) where the rows are ‘documents’ (i.e. Facebook comments in our dataset), and the columns are ‘terms’ (i.e. each unique word found across all the documents in the dataset). The ‘cells’ (i.e. elements) of the matrix indicate how many times term n occurred in document m.
Note: we use the control argument to specify that we only want to retain words that are minimum character length of 3, up to a maximum of 20 characters.
dtm <- DocumentTermMatrix(fbCorpus,control = list(wordLengths=c(3, 20)))
dtm
<<DocumentTermMatrix (documents: 220, terms: 816)>>
Non-/sparse entries: 1564/177956
Sparsity : 99%
Maximal term length: 17
Weighting : term frequency (tf)
What we have is a sparse matrix, i.e. most of the elements of the matrix are 0, i.e. in our dataset most Facebook comments contain only a small percentage of ‘vocabulary’ of terms observed across the entire set of comments. What we want to do is remove terms that occur very infrequently, which will leave us with the most ‘important’ terms. We remove sparse terms using the removeSparseTerms function, which removes terms that occur equal to or less than a percentage threshold.
For example, if we set it to 0.995, then all terms that are at least 99.5% sparse are removed. The following command lets us ‘test out’ what our document-term matrix would look like if we set the threshold to 0.995:
removeSparseTerms(dtm, 0.995)
<<DocumentTermMatrix (documents: 220, terms: 249)>>
Non-/sparse entries: 997/53783
Sparsity : 98%
Maximal term length: 11
Weighting : term frequency (tf)
0.995 will do the trick for us in this workshop, so we will create a new document-term matrix with this threshold applied to it:
dtmSparseRemoved <- removeSparseTerms(dtm, 0.995)
We can examine term frequencies in our data. We create a character vector of the sums of columns of our document-term matrix (implicitly coercing it to a matrix object), meaning that have a named character vector where the names are the unique terms in our document-term matrix, and the values of the elements are the number of times that particular word occurs across all of our corpus.
freqTerms <- colSums(as.matrix(dtmSparseRemoved))
freqTerms
actual admir allianc also alway amaz among anoth anyon anyth
4 2 2 3 5 3 2 4 2 3
argument arm artist ask atat attack awesom back background bad
2 2 2 4 2 3 4 3 2 2
base battl beauti behind believ best big blew blow brian
3 4 3 8 2 2 4 2 3 2
bring budget build built butterfli call came can cant choke
2 3 16 2 4 2 4 6 2 2
come command complet construct contractor cool couldnt dainti darth dead
2 4 3 8 10 3 4 3 3 2
death deathstar definit destroy destruct detail didnt die dish dont
44 2 2 11 4 3 6 2 2 5
doubl effort elabor emperor empir endor enemi episod even ever
2 4 2 17 21 6 2 3 4 3
everyth evil ewok expect face fail falcon favorit find finish
2 5 3 2 2 2 2 3 2 8
first flight forc forgiv found fulli get good got govern
16 2 6 3 2 6 5 11 4 2
great ground hadnt half hate help hope hundr imagin imperi
2 2 2 4 3 2 4 3 2 3
independ instal issu jedi job juan just keep kill kind
2 2 2 3 2 2 15 2 4 2
knew know knowledg land lando laser learn led let life
2 10 2 6 2 2 4 2 2 2
like live lol long look lost love luke made main
15 2 4 4 4 3 9 2 3 2
make man manag mani mass may mayb men moment moon
11 4 2 7 2 2 4 3 2 2
movi much need new nice now old one oper origin
11 2 4 9 3 5 2 17 7 6
paint peopl permiss pic pictur pilot plan planet pleas poor
6 3 4 2 7 2 4 3 2 3
poster power probabl project rather realli rebel rebellion redoubl releas
2 5 4 6 2 5 11 5 2 2
rememb return right run saw say schedul screen second secur
2 2 3 2 4 2 7 2 6 2
see shield ship shot shouldnt show shuttl sick size someth
7 3 5 2 2 2 7 2 2 5
space span star start station steal stormtroop straight strike superweapon
4 2 51 3 3 2 3 2 3 2
sure take target tarkin thank that there thing think thought
4 3 2 2 2 8 5 11 9 8
thrawn time toilet took trap tri trilog turn two unfinish
2 10 2 8 10 3 2 3 3 3
union univers use vader version visit vong wait wall wallpap
4 2 3 9 2 3 2 2 2 2
want war wasnt watch way weak weapon well werent will
5 9 6 5 3 3 4 5 2 5
william wish without work worker wouldnt yeah year yuuzhan
2 3 2 6 3 2 2 9 2
We order the term frequencies and look at the 5 most frequent terms and then 5 least frequent terms:
orderTerms <- order(freqTerms,decreasing=TRUE)
freqTerms[head(orderTerms)]
star death empir emperor one build
51 44 21 17 17 16
freqTerms[tail(orderTerms)]
werent william without wouldnt yeah yuuzhan
2 2 2 2 2 2
Which terms occurred at least 20 times?
findFreqTerms(dtmSparseRemoved, 20)
[1] "death" "empir" "star"
We can do a basic correlation analysis by looking at the correlations between terms with the findAssocs function. If two words always appear together then corr = 1. If two terms never appear together then corr = 0. Let’s look at which terms co-occur with the term “meat”, with a lower correlation limit of 0.5.
findAssocs(dtmSparseRemoved, "good", corlimit=0.5)
$good
evil anyon believ can expect kind life return without anyth ever peopl start turn
0.87 0.68 0.68 0.68 0.68 0.68 0.68 0.68 0.68 0.55 0.55 0.55 0.55 0.55
Next, we can do some text visualisation. First, we can plot our descriptive statistics in various ways. For example, using a barchart to visualise the 20 most frequent terms (we will use the lattice package for a nice bar chart:
require(lattice)
Loading required package: lattice
# png("barchart_frequent_terms.png", width=800, height=700)
barchart(freqTerms[orderTerms[1:20]])

# dev.off()
Word Cloud
Next, we will construct a comparison word cloud of the Star Wars and Star Trek fan page comments.
# create a character vector of the Star Wars comments
# (i.e., take a subset of elements from the commentText column of the dataframe)
starWarsComments <- myStarWarsData$commentText[which(myStarWarsData$commentText!="Not_applicable")]
starWarsComments <- paste(starWarsComments , collapse = " ")
# do the same, but for Star Trek
starTrekComments <- myStarTrekData$commentText[which(myStarTrekData$commentText!="Not_applicable")]
starTrekComments <- paste(starTrekComments , collapse = " ")
# combine them together into a dataframe
df_ALL <- data.frame(group=c("Star_Wars","Star_Trek"),words=c(starWarsComments,starTrekComments))
# search for any texts that have no characters (i.e. are ’empty’)
# and then remove these elements from the vector
toRemove <- which(df_ALL$words=="")
Data pre-processing:
# search for any texts that have no characters (i.e. are ’empty’)
# and then remove these elements from the vector
toRemove <- which(df_ALL$words=="")
# are there any ’empty’ text elements?
# (i.e. length of toRemove is not equal to zero)
# if true, then we remove the corresponding rows from the dataframe
if (isTRUE(length(toRemove)!=0)) {
df_ALL <- df_ALL[-toRemove,]
}
# we create a character vector from the "words" column of df_ALL
# this will be our independent variable.
# we do not want text as factors, so we will coerce it to character
words <- df_ALL$words
# we will convert the character encoding to UTF-8
# just to be sure there are no odd characters that
# may cause problems later on
words <- iconv(words, to = "UTF-8")
# ** MAC USERS ONLY **:
words <- iconv(words, to = "UTF-8-mac")
# using ’tm’ package we convert character vector to a Vcorpus object (volatile corpus)
corp <- VCorpus(VectorSource(words))
## now we do transformations of text using tm_map (’mapping to the corpus’)
# eliminate extra whitespace
corp <- tm_map(corp, stripWhitespace)
# convert to all lowercase
corp <- tm_map(corp, content_transformer(tolower))
# perform stemming (not always useful!)
#fbCorpus <- tm_map(fbCorpus, stemDocument)
# remove numbers (not always useful!)
fbCorpus <- tm_map(fbCorpus, removeNumbers)
# remove punctuation (not always useful! e.g. text emoticons)
fbCorpus <- tm_map(fbCorpus, removePunctuation)
# remove stop words (not always useful!) - doing this in perl
corp <- tm_map(corp, removeWords, stopwords("english"))
# create a document-term matrix
# had to do it this way to be able to use colnames
tdm <- TermDocumentMatrix(corp)
tdm <- as.matrix(tdm)
#print(tdm)
colnames(tdm) <- c("Star_Wars","Star_Trek")
colorsx=c("blue","red")
Word Cloud visualization:
require(wordcloud)
Loading required package: wordcloud
#note: if changing res of png, can’t have dimensions in pixels (led to wordclouds with very few words...)
# png("facebook_starwars_startrek_comparison_cloud.png", width=12, height=8, units="in", res=300)
#comparison.cloud(tdm,max.words=300,random.order=FALSE)
comparison.cloud(tdm,max.words=200,random.order=FALSE,colors=colorsx)

#commonality.cloud(tdm,random.order=FALSE)
# dev.off()
LS0tCnRpdGxlOiAiU29jaWFsTWVkaWFMYWIgLSBTb2NpYWwgTmV0d29yayBBbmFseXNpcyAoU05BKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogIHBkZl9kb2N1bWVudDoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleAogICAgdG9jOiB5ZXMKICB3b3JkX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgojIFNvY2lhbCBNZWRpYSBEYXRhIENvbGxlY3Rpb24KCiMjIENvbGxlY3RpbmcgVHdpdHRlciBkYXRhIGFuZCBjcmVhdGluZyBzb2NpYWwgbmV0d29ya3MKCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHJ1biB0aHJvdWdoIGhvdyB0byBjb2xsZWN0IGRhdGEgZnJvbSBUd2l0dGVyLCBjcmVhdGUgbmV0d29ya3MsIGFuZCBwZXJmb3JtIGRpZmZlcmVudApraW5kcyBvZiBhbmFseXNpcy4KCkl0IGlzIGN1cnJlbnRseSBwb3NzaWJsZSB0byBjcmVhdGUgMyBkaWZmZXJlbnQgdHlwZXMgb2YgbmV0d29ya3MgdXNpbmcgVHdpdHRlciBkYXRhIGNvbGxlY3RlZCB3aXRoIFNvY2lhbE1lZGlhTGFiLgpUaGVzZSBhcmUgKDEpIGFjdG9yIG5ldHdvcmtzOyAoMikgYmltb2RhbCBuZXR3b3JrczsgYW5kICgzKSBzZW1hbnRpYyBuZXR3b3Jrcy4gCgpGaXJzdCwgZGVmaW5lIHRoZSBBUEkgY3JlZGVudGlhbHMuIER1ZSB0byB0aGUgVHdpdHRlciBBUEkgc3BlY2lmaWNhdGlvbnMsIGl0IGlzIG5vdCBwb3NzaWJsZSB0byBzYXZlIGF1dGhlbnRpY2F0aW9uCnRva2VucyBiZXR3ZWVuIHNlc3Npb25zLiBUaGUgQXV0aGVudGljYXRlKCkgZnVuY3Rpb24gaXMgY2FsbGVkIG9ubHkgZm9yIGl0cyBzaWRlIGVmZmVjdCwgd2hpY2ggcHJvdmlkZXMgYWNjZXNzCnRvIHRoZSBUd2l0dGVyIEFQSSBmb3IgdGhlIGN1cnJlbnQgc2Vzc2lvbi4KCiMjIyBBdXRoZW50aWNhdGluZyB3aXRoIHRoZSBUd2l0dGVyIEFQSQoKR28gdG8gVHdpdHRlciBbQXBwbGljYXRpb24gTWFuYWdlbWVudF0oaHR0cHM6Ly9hcHBzLnR3aXR0ZXIuY29tLykgYW5kICJDcmVhdGUgTmV3IEFwcCIuIENvbXBsZXRlIGFsbCBmaWVsZHMgaW4gdGhlIGZvcm0gYW5kIGNyZWF0ZSBhIG5ldyBhcHAuIEluIHlvdXIgYXBwIHBhZ2UsIGdvIHRvICJLZXlzIGFuZCBBY2Nlc3MgVG9rZW5zIiwgZ2VuZXJhdGUgeW91ciBhY2Nlc3MgdG9rZW4sIGFuZCBjb3B5IHRoZSBpbmZvcm1hdGlvbiB0byBSOgpgYGB7cn0KbXlhcGlrZXkgPC0gIjgwMXhVNDFMeFptSHJOaDdOWTN3Vm5CN0MiICMgUHV0IHlvdXIgb3duIGFwaSBpbmZvIGhlcmUKbXlhcGlzZWNyZXQgPC0gIkU0c0FUQmpUM1pTQVJnaG1YZ2pyZGNPWlA1clZXSFZYRnhBd3h2b0cyNFFMTjVvRFFnIiAjIFB1dCB5b3VyIG93biBhcGkgaW5mbyBoZXJlCm15YWNjZXNzdG9rZW4gPC0gIjE5ODU2NzMwLVhIRE91TlcwM0Q2R3FqdjJKRFFGeGhabXVyZHNUZTU1bkxNVTQ4a2pxIiAjIFB1dCB5b3VyIG93biBhcGkgaW5mbyBoZXJlCm15YWNjZXNzdG9rZW5zZWNyZXQgPC0gIjJ4RllyRk8xeXQxSDBXemdIRWp6cW9qZjZUaE5lQmZDMGYwYllKQjJrd2FRWiIgIyBQdXQgeW91ciBvd24gYXBpIGluZm8gaGVyZQpgYGAKCkdpdmVuIHRoYXQgd2UgYXJlIGdvaW5nIHRvIGJlIGNyZWF0aW5nIHR3byBkaWZmZXJlbnQgdHlwZXMgb2YgVHdpdHRlciBuZXR3b3JrcyAoYWN0b3IgYW5kIHNlbWFudGljKSwgd2Ugd2lsbApDb2xsZWN0KCkgdGhlIGRhdGEsIGJ1dCBub3QgcGlwZSBpdCBkaXJlY3RseSB0aHJvdWdoIHRvIE5ldHdvcmsoKSBzdHJhaWdodCBhd2F5LiBUaGlzIG1lYW5zIHdlIGNhbiByZXVzZQp0aGUgZGF0YSBtdWx0aXBsZSB0aW1lcyB0byBjcmVhdGUgdHdvIGRpZmZlcmVudCBraW5kcyBvZiBuZXR3b3JrcyBmb3IgYW5hbHlzaXMuCldlIHdpbGwgY29sbGVjdCAxNTAgcmVjZW50IHR3ZWV0cyB0aGF0IGhhdmUgdXNlZCB0aGUgI2F1c3BvbCBoYXNodGFnLiBUaGlzIGlzIHRoZSBkb21pbmFudCBoYXNodGFnIGZvcgpBdXN0cmFsaWFuIHBvbGl0aWNzLgpUaGUgZmlyc3Qgc3RlcCBpbiB0aGUgd29yayBmbG93IGlzIHRvIGF1dGhvcmlzZSBhY2Nlc3MgdGhlIFR3aXR0ZXIgQVBJLiBJbnN0cnVjdGlvbnMgZm9yIG9idGFpbmluZyBUd2l0dGVyIEFQSQphY2Nlc3MgYXJlIGF2YWlsYWJsZSBmcm9tIHRoZSBWT1NPTiB3ZWJzaXRlLiBTZWUgdGhlIHByZXZpb3VzIHNlY3Rpb24gZm9yIGEgYnJpZWYgZXhwbGFuYXRpb24gb2YgQVBJcy4KYGBge3J9CnJlcXVpcmUoU29jaWFsTWVkaWFMYWIpCnJlcXVpcmUobWFncml0dHIpCm15VHdpdHRlckRhdGEgPC0gQXV0aGVudGljYXRlKCJ0d2l0dGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBpS2V5PW15YXBpa2V5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcGlTZWNyZXQ9bXlhcGlzZWNyZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY2Vzc1Rva2VuPW15YWNjZXNzdG9rZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY2Vzc1Rva2VuU2VjcmV0PW15YWNjZXNzdG9rZW5zZWNyZXQpICU+JQpDb2xsZWN0KHNlYXJjaFRlcm09IiNhdXNwb2wiLCBudW1Ud2VldHM9MTUwLCB3cml0ZVRvRmlsZT1GQUxTRSwgdmVyYm9zZT1UUlVFKQpgYGAKCldlIGNhbiBoYXZlIGEgcXVpY2sgbG9vayBhdCB0aGUgZGF0YSB3ZSBqdXN0IGNvbGxlY3RlZDoKYGBge3J9ClZpZXcobXlUd2l0dGVyRGF0YSkKYGBgCgpOb3RlIHRoZSBjbGFzcyBvZiB0aGUgZGF0YWZyYW1lLCB3aGljaCBsZXRzIFNvY2lhbE1lZGlhTGFiIGtub3cgdGhhdCB0aGlzIGlzIGFuIG9iamVjdCBvZiBjbGFzcyBkYXRhU291cmNlICwKd2hpY2ggd2UgY2FuIHRoZW4gcGFzcyB0byB0aGUgQ3JlYXRlKCkgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgZGlmZmVyZW50IGtpbmRzIG9mIG5ldHdvcmtzOgoKYGBge3J9CmNsYXNzKG15VHdpdHRlckRhdGEpCmBgYAoKSWYgeW91IGZpbmQgdGhhdCB5b3UgYXJlIGVuY291bnRlcmluZyBlcnJvcnMgcG9zc2libHkgcmVsYXRlZCB0byB0aGUgdGV4dCBvZiB0aGUgdHdlZXRzLCB5b3UgY2FuIHRyeSBjb252ZXJ0aW5nCnRoZSB0d2VldCB0ZXh0IHRvIFVURi04IGNoYXJhY3RlciBlbmNvZGluZy4gUm91Z2hseSBzcGVha2luZywgdGhpcyBjb21tYW5kIHdpbGwgaGVscCB0byBkZWFsIHdpdGggJ29kZCcKY2hhcmFjdGVycyBpbiB0aGUgdGV4dC4KYGBge3J9Cm15VHdpdHRlckRhdGEkdGV4dCA8LSBpY29udihteVR3aXR0ZXJEYXRhJHRleHQsIHRvID0gJ3V0Zi04JykKYGBgCgpNYWMgdXNlcnMgb25seSBtYXkgYWxzbyB3aXNoIHRvIHRyeSB0aGUgZm9sbG93aW5nIGlmIHRoZXkgYXJlIGVuY291bnRlcmluZyBlcnJvcnMgdGhhdCBtYXkgYmUgZHVlIHRvCmNoYXJhY3RlciBlbmNvZGluZyBpc3N1ZXM6CmBgYHtyfQpteVR3aXR0ZXJEYXRhJHRleHQgPC0gaWNvbnYobXlUd2l0dGVyRGF0YSR0ZXh0LCB0byA9ICd1dGYtOC1tYWMnKQpgYGAKCiMjIyBDcmVhdGluZyBzb2NpYWwgbmV0d29ya3Mgd2l0aCBUd2l0dGVyIGRhdGEKCiMjIyMgQWN0b3IgbmV0d29yawpGaXJzdCwgd2Ugd2lsbCBjcmVhdGUgYW4gYWN0b3IgbmV0d29yay4gSW4gdGhpcyBhY3RvciBuZXR3b3JrLCBlZGdlcyByZXByZXNlbnQgaW50ZXJhY3Rpb25zIGJldHdlZW4gVHdpdHRlcgp1c2Vycy4gQW4gaW50ZXJhY3Rpb24gaXMgZGVmaW5lZCBhcyBhICJtZW50aW9uIiIgb3IgInJlcGx5IiIgb3IgInJldHdlZXQiIGZyb20gdXNlciBpIHRvIHVzZXIgaiwgZ2l2ZW4gInR3ZWV0IiBtLiBJbgphIG51dHNoZWxsLCBhIFR3aXR0ZXIgYWN0b3IgbmV0d29yayBzaG93cyB1cyB3aG8gaXMgaW50ZXJhY3Rpbmcgd2l0aCB3aG8gaW4gcmVsYXRpb24gdG8gYSBwYXJ0aWN1bGFyIGhhc2h0YWcKb3Igc2VhcmNoIHRlcm0uCmBgYHtyfQpnX3R3aXR0ZXJfYWN0b3IgPC0gbXlUd2l0dGVyRGF0YSAlPiUgQ3JlYXRlKCJBY3RvciIpCmBgYAoKV2UgY2FuIG5vdyBleGFtaW5lIHRoZSBkZXNjcmlwdGlvbiBvZiBvdXIgbmV0d29yazoKYGBge3J9CmdfdHdpdHRlcl9hY3RvcgpgYGAKCiMjIyMgU2VtYW50aWMgbmV0d29yawoKWy8vXTogIyAoCm15VHdpdHRlckRhdGEgPC0gcmVhZC5jc3YoIk9jdF8wMl8xNV8wOF8zOF8yMDE3X0NFU1RfI2F1c3BvbF9Ud2l0dGVyRGF0YS5jc3YiKQopCgpOZXh0LCB3ZSB3aWxsIGNyZWF0ZSBhIHNlbWFudGljIG5ldHdvcmsuIEluIHRoaXMgbmV0d29yayBub2RlcyByZXByZXNlbnQgdW5pcXVlIGNvbmNlcHRzIChpbiB0aGlzIGNhc2UKdW5pcXVlIHRlcm1zL3dvcmRzIGV4dHJhY3RlZCBmcm9tIGEgc2V0IG9mIDE1MCB0d2VldHMpLCBhbmQgZWRnZXMgcmVwcmVzZW50IHRoZSBjby1vY2N1cnJlbmNlIG9mIHRlcm1zIGZvciBhbGwKb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhIHNldC4gRm9yIGV4YW1wbGUsIGZvciB0aGlzIFR3aXR0ZXIgc2VtYW50aWMgbmV0d29yaywgbm9kZXMgcmVwcmVzZW50IGVpdGhlciBoYXNodGFncwooZS5nLiAiI2F1c3BvbCIpIG9yIHNpbmdsZSB0ZXJtcyAoInBvbGl0aWNzIikuIElmIHRoZXJlIGFyZSAxNTAgdHdlZXRzIGluIHRoZSBkYXRhIHNldCAoaS5lLiAxNTAgb2JzZXJ2YXRpb25zKSwKYW5kIHRoZSB0ZXJtICNhdXNwb2wgYW5kIHRoZSB0ZXJtIHBvbGl0aWNzIGFwcGVhciB0b2dldGhlciBpbiBldmVyeSB0d2VldCwgdGhlbiB0aGlzIHdvdWxkIGJlIHJlcHJlc2VudGVkCmJ5IGFuIGVkZ2Ugd2l0aCB3ZWlnaHQgZXF1YWwgdG8gMTUwCmBgYHtyfQpnX3R3aXR0ZXJfc2VtYW50aWMgPC0gbXlUd2l0dGVyRGF0YSAlPiUgQ3JlYXRlKCJTZW1hbnRpYyIpCmBgYAoKTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIG5ldHdvcmsgZGVzY3JpcHRpb246CmBgYHtyfQpnX3R3aXR0ZXJfc2VtYW50aWMKYGBgCgojIyMjIEJpbW9kYWwgbmV0d29yawoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgVHdpdHRlciBkYXRhIHdlIGNhbiBnZW5lcmF0ZSBhIGJpbW9kYWwgbmV0d29yay4gVGhpcyBraW5kIG9mIG5ldHdvcmsgcHJvdmlkZXMKbWFueSBwb3NzaWJpbGl0aWVzIGZvciBhbmFseXNpcyBhbmQgZ2VuZXJhdGluZyBpbnNpZ2h0cyBmcm9tIG91ciBkYXRhLgoKSW4gdGhpcyBiaW1vZGFsIG5ldHdvcmsgdGhlcmUgYXJlIHR3byB0eXBlcyBvZiBub2RlczogdXNlcnMgYW5kIGhhc2h0YWdzLiBUaGUgYmltb2RhbCBuZXR3b3JrIGlzIHRoZXJlZm9yZToKCiogZGlyZWN0ZWQgKHVzZXJzIGNhbiB1c2UgaGFzaHRhZ3MsIGJ1dCBoYXNodGFncyBjYW4ndCB1c2UgdXNlcnMpCiogd2VpZ2h0ZWQgKHVzZXJzIGNhbiBjb21tZW50IG11bHRpcGxlIHRpbWVzIG9uIGEgcG9zdCkKKiBiaXBhcnRpdGUgKHVzZXJzIGNhbiBsaWtlIG9yIGNvbW1lbnQgb24gcG9zdHMsIGJ1dCBwb3N0cyBjYW4ndCBsaWtlIG9yIGNvbW1lbnQgYmFjaykKKiBtdWx0aXBsZSBlZGdlcyBvciBwYXJhbGxlbCBlZGdlcyAod2UgaGF2ZSBvbmUgZWRnZSBmb3IgZWFjaCBpbnRlcmFjdGlvbiBmcm9tIHVzZXIgaSB0byBwb3N0IGopCgpXZSBub3cgcnVuIHRoZSBDcmVhdGUoKSBmdW5jdGlvbiwgd2hpY2ggY3JlYXRlcyBhbiBpZ3JhcGggb2JqZWN0IGNhbGxlZCBnX2JpbW9kYWxfdHdpdHRlci4gQ3JlYXRpbmcKbmV0d29ya3MgaW4gU29jaWFsTWVkaWFMYWIgaXMgc3RyYWlnaHRmb3J3YXJkLiBXZSBzaW1wbHkgcGFzcyB0aGUgbXlUd2l0dGVyRGF0YSBvYmplY3QgdG8gdGhlIENyZWF0ZSgpCmZ1bmN0aW9uLCBhbmQgaXQgdGFrZXMgY2FyZSBvZiB0aGUgcmVzdC4gV2Ugc3BlY2lmeSB3aGF0IGtpbmQgb2YgbmV0d29yayB3ZSB3YW50IHRvIGNyZWF0ZSAoaS5lLiBhIGJpbW9kYWwKbmV0d29yaykgYnkgc3BlY2lmeWluZyB0aGlzIGFzIGFuIGFyZ3VtZW50IHRvIHRoZSBDcmVhdGUoKSBmdW5jdGlvbi4KCk5vdGUgYWxzbyB0aGF0IHRoZXJlIGlzIGEgdHJpY2t5IG9wZXJhdG9yIGludHJvZHVjZWQgaGVyZSwgdGhlICdwaXBlJyBvcGVyYXRvciAlPiUgLCB3aGljaCB3ZSBoYXZlIG5vdCBjb3ZlcmVkIHlldC4gVGhpcyBvcGVyYXRvciBjb21lcyBmcm9tIHRoZSBNYWdyaXR0ciBwYWNrYWdlLCBhbmQgaXQgaXMgdXNlZCB0byAncGlwZScgdG9nZXRoZXIgY29tbWFuZHMgaW4gYSBjaGFpbiwgcGFzc2luZyB0aGUgdmFsdWVzIGFsb25nIHRoZSBwaXBlbGluZSB1bnRpbCBpdCByZWFjaGVzIHRoZSBmaW5hbCBjb21tYW5kLCB3aGljaCByZXR1cm5zIHRoZSBvdXRwdXQgKGkuZS4gdGhlIG5ldHdvcmsgd2Ugd2lzaCB0byBjcmVhdGUpLiBJbiB0aGlzIGluc3RhbmNlIHdlIGFyZSBwYXNzaW5nIChvciAicGlwaW5nIikgdGhlIGRhdGEgd2UgY29sbGVjdGVkIHVzaW5nIENvbGxlY3QoKSB0aHJvdWdoIHRvIHRoZSBDcmVhdGUoKQpmdW5jdGlvbi4KYGBge3J9CmdfYmltb2RhbF90d2l0dGVyIDwtIG15VHdpdHRlckRhdGEgJT4lIENyZWF0ZSgiQmltb2RhbCIpCmBgYAoKV2UgY2FuIG5vdyB2aWV3IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSBuZXR3b3JrOgpgYGB7cn0KZ19iaW1vZGFsX3R3aXR0ZXIKYGBgCgojIyBDb2xsZWN0aW5nIEZhY2Vib29rIGRhdGEKCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHJ1biB0aHJvdWdoIGhvdyB0byBjb2xsZWN0IGRhdGEgZnJvbSBGYWNlYm9vaywgY3JlYXRlIG5ldHdvcmtzLCBhbmQgcGVyZm9ybSBkaWZmZXJlbnQKa2luZHMgb2YgYW5hbHlzaXMuCgojIyMgQXV0aGVudGljYXRlIHdpdGggRmFjZWJvb2sgRGV2ZWxvcGVycyBBUEkKClRoZSBwcm9jZXNzIG9mIGF1dGhlbnRpY2F0aW9uLCBkYXRhIGNvbGxlY3Rpb24sIGFuZCBjcmVhdGluZyBzb2NpYWwgbmV0d29ya3MgY2FuIGJlIGV4cHJlc3NlZCB3aXRoIHRoZSAzIHZlcmIKZnVuY3Rpb25zOiBBdXRoZW50aWNhdGUoKSAsIENvbGxlY3QoKSAsIGFuZCBDcmVhdGUoKSAuIFRoaXMgc2ltcGxpZmllZCB3b3JrZmxvdyBleHBsb2l0cyB0aGUgcGlwZSBpbnRlcmZhY2UKb2YgdGhlIE1hZ3JpdHRyIHBhY2thZ2UsIGFuZCBwcm92aWRlcyBiZXR0ZXIgaGFuZGxpbmcgb2YgQVBJIGF1dGhlbnRpY2F0aW9uIGJldHdlZW4gUiBzZXNzaW9ucy4gCldoYXQgd2UgYXJlIGRvaW5nIGlzICJwaXBpbmciIHRoZSBkYXRhIGZvcndhcmQgdXNpbmcgdGhlICU+JSBvcGVyYXRvciwgaW4gYSBraW5kIG9mIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcKYXBwcm9hY2guIEl0IG1lYW5zIHdlIGNhbiBwaXBlIHRvZ2V0aGVyIGFsbCB0aGUgZGlmZmVyZW50IGVsZW1lbnRzIG9mIHRoZSB3b3JrIGZsb3cgaW4gYSBxdWljayBhbmQgZWFzeQptYW5uZXIuIFRoaXMgYWxzbyBwcm92aWRlcyB0aGUgYWJpbGl0eSB0byBzYXZlIGFuZCBsb2FkIGF1dGhlbnRpY2F0aW9uIHRva2Vucywgc28gd2UgZG9u4oCZdCBoYXZlIHRvIGtlZXAgYXV0aGVudGljYXRpbmcKd2l0aCBBUElzIGJldHdlZW4gc2Vzc2lvbnMuIE9idmlvdXNseSwgdGhpcyBvcGVucyB1cCBwb3NzaWJpbGl0aWVzIGZvciBhdXRvbWF0aW9uIGFuZCBkYXRhIG1pbmluZyBwcm9qZWN0cy4KCkdvIHRvIEZhY2Vib29rIERldmVsb3BlcnMgUGFnZSAoaHR0cHM6Ly9kZXZlbG9wZXJzLmZhY2Vib29rLmNvbS9hcHBzLyksIGNyZWF0ZSBhIG5ldyBhcHAsIGFuZCBjb3B5IHRoZSBhcHBJRCBhbmQgYXBwU2VjcmV0IGluZm9ybWF0aW9uLgoKTWFrZSBzdXJlIHdlIGhhdmUgb3VyIGFwcElEIGFuZCBhcHBTZWNyZXQgdmFsdWVzIGRlZmluZWQ6CmBgYHtyfQphcHBJRCA8LSAiMjAyNTIwNDE2NzY5ODExNCIgIyBQdXQgeW91ciBvd24gYXBpIGluZm8gaGVyZQphcHBTZWNyZXQgPC0gIjNhMGI4ZjMyOWUxZWQ5ZDgwM2UwMmI1YjNmZGFlODMxIiAjIFB1dCB5b3VyIG93biBhcGkgaW5mbyBoZXJlCmBgYAoKU2F2ZSBjcmVkZW50aWFsIGZpbGUgZm9yIGxhdGVyIGFjY2VzczoKYGBge3IgZXZhbD1GQUxTRX0KcmVxdWlyZShTb2NpYWxNZWRpYUxhYikKcmVxdWlyZShtYWdyaXR0cikKQXV0aGVudGljYXRlKCJGYWNlYm9vayIsIGFwcElEID0gYXBwSUQsIGFwcFNlY3JldCA9IGFwcFNlY3JldCkgJT4lIFNhdmVDcmVkZW50aWFsKCJGQkNyZWRlbnRpYWwuUkRTIikKYGBgCgpUaGUgZmlyc3QgdGltZSB5b3UgYXV0aGVudGljYXRlIHlvdSB3aWxsIHNlZSB0aGlzOgoKPkNvcHkgYW5kIHBhc3RlIGludG8gU2l0ZSBVUkwgb24gRmFjZWJvb2sgQXBwIFNldHRpbmdzOiBodHRwOi8vbG9jYWxob3N0OjE0MTAvCgpZb3UgaGF2ZSB0byBwYXN0ZSB1cmwgKGh0dHA6Ly9sb2NhbGhvc3Q6MTQxMC8gKSBpbiBkZXZlbG9wZXIgYXBwIHNldHRpbmdzOiAoaSkgQ2xpY2sgb24gYWRkIHBsYXRmb3JtLiAoaWkpIENob29zZSB3ZWJzaXRlIGFuZCBwYXN0ZSBodHRwOi8vbG9jYWxob3N0OjE0MTAvIGluIFVSTCBsaW5rLgoKIyMjIEJpbW9kYWwgbmV0d29ya3MKCkZpcnN0LCB3ZSB3aWxsIGNvbGxlY3QgMiBkYXlzIHdvcnRoIG9mIGFjdGl2aXR5IGZyb20gdGhlIFN0YXIgV2FycyBvZmZpY2lhbCBwYWdlLiBUaGlzIHdpbGwgY29sbGVjdCBhbGwgdGhlIHBvc3RzCnBvc3RlZCBiZXR3ZWVuIHRoZSByYW5nZUZyb20gYW5kIHJhbmdlVG8gZGF0ZXMsIGluY2x1ZGluZyBhbGwgY29tbWVudHMgYW5kIGxpa2VzLCBhbmQgb3RoZXIgYXNzb2NpYXRlZApkYXRhIGluY2x1ZGluZyB1c2VybmFtZXMsIHRpbWVzdGFtcHMgZm9yIGNvbW1lbnRzLCBldGMuIE5vdGU6IHRoZSBkYXRlIGZvcm1hdCBpcyBZWVlZLU1NLURELgoKV2Ugd2lsbCBiZSB1c2luZyB0aGlzIGRhdGEgdG8gY3JlYXRlIGEgYmltb2RhbCBuZXR3b3JrLiBUaGlzIGdyYXBoIG9iamVjdCBpcyBiaW1vZGFsIGJlY2F1c2UgZWRnZXMgcmVwcmVzZW50CnJlbGF0aW9uc2hpcHMgYmV0d2VlbiBub2RlcyBvZiB0d28gZGlmZmVyZW50IHR5cGVzLiBGb3IgZXhhbXBsZSwgaW4gb3VyIGJpbW9kYWwgRmFjZWJvb2sgbmV0d29yaywgbm9kZXMKcmVwcmVzZW50IEZhY2Vib29rIHVzZXJzIG9yIEZhY2Vib29rIHBvc3RzLCBhbmQgZWRnZXMgcmVwcmVzZW50IHdoZXRoZXIgYSB1c2VyIGhhcyBjb21tZW50ZWQgb3Ig4oCYbGlrZWTigJkgYQpwb3N0LiBFZGdlcyBhcmUgZGlyZWN0ZWQgYW5kIHdlaWdodGVkIChlLmcuIGlmIHVzZXIgaSBoYXMgY29tbWVudGVkIG4gdGltZXMgb24gcG9zdCBqLCB0aGVuIHRoZSB3ZWlnaHQgb2YgdGhpcwpkaXJlY3RlZCBlZGdlIGVxdWFscyBuKS4KCmBgYHtyfQpnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzIDwtIAogIExvYWRDcmVkZW50aWFsKCJGQkNyZWRlbnRpYWwuUkRTIikgJT4lIAogIENvbGxlY3QocGFnZU5hbWU9IlN0YXJXYXJzIiwgcmFuZ2VGcm9tPSIyMDE1LTAzLTAxIiwgcmFuZ2VUbz0iMjAxNS0wMy0wMiIsIHdyaXRlVG9GaWxlPVRSVUUpICU+JSAKICBDcmVhdGUoIkJpbW9kYWwiKQpgYGAKClRoZSBNYWdyaXR0ciBwaXBlIGFwcHJvYWNoIHVzZWQgaW4gdGhpcyBleGFtcGxlIG1lYW5zIHRoYXQgd2Ugb25seSBlbmQgdXAgd2l0aCB0aGUgZmluYWwgZ3JhcGggb2JqZWN0IChpbgp0aGUgZ2xvYmFsIGVudmlyb25tZW50KS4gVG8gZW5zdXJlIHdlIHJldGFpbiB0aGUgZGF0YSB0aGF0IGFyZSBjb2xsZWN0ZWQsIHRoZSBhcmd1bWVudCB3cml0ZVRvRmlsZT1UUlVFIGlzCnVzZWQuIFRoaXMgd3JpdGVzIHRoZSBkYXRhIGNvbGxlY3RlZCB1c2luZyBDb2xsZWN0KCkgZnVuY3Rpb24gdG8gYSBsb2NhbCBDU1YgZmlsZSBiZWZvcmUgaXQgaXMgcGlwZWQgdGhyb3VnaCB0bwp0aGUgbmV0d29yayBnZW5lcmF0aW9uIGZ1bmN0aW9uIENyZWF0ZSgpIC4gV2UgY2FuIHRoZW4gcmVhZCBpdCBpbiBhcyBhIGRhdGFmcmFtZSAoc2VlIGNvZGUgc25pcHBldCBiZWxvdykuCmBgYHtyfQojIG15U3RhcldhcnNEYXRhIDwtIHJlYWQuY3N2KCIyMDE1LTAzLTAxX3RvXzIwMTUtMDMtMDJfU3RhcldhcnNfRmFjZWJvb2tEYXRhLmNzdiIpCm15U3RhcldhcnNEYXRhIDwtIGltcG9ydERhdGEoIjIwMTUtMDMtMDFfdG9fMjAxNS0wMy0wMl9TdGFyV2Fyc19GYWNlYm9va0RhdGEuY3N2IiwgImZhY2Vib29rIikKVmlldyhteVN0YXJXYXJzRGF0YSkKZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfd2FycyA8LSBteVN0YXJXYXJzRGF0YSAlPiUgQ3JlYXRlKCJCaW1vZGFsIikKYGBgCgpUaGlzIG1lYW5zIHdlIGVuZCB1cCB3aXRoIHR3byBvYmplY3RzIGZvciBmdXJ0aGVyIGFuYWx5c2lzLCBhIGdyYXBoIG9iamVjdCBnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzICwKYW5kIGEgZGF0YWZyYW1lIG9iamVjdCBteVN0YXJXYXJzRGF0YS4KCkJlZm9yZSBwcm9jZWVkaW5nIGludG8gYW5hbHlzaXMsIHdlIHdpbGwgY29sbGVjdCAyIGRheXMgd29ydGggb2YgZGF0YSBmcm9tIHRoZSBTdGFyIFRyZWsgRmFjZWJvb2sgcGFnZSwgYnV0CnRoaXMgdGltZSB3ZSB3aWxsIHBpcGUgdGhyb3VnaCB0aGUgTG9hZENyZWRlbnRpYWwgZnVuY3Rpb24sIG1lYW5pbmcgdGhhdCB3ZSBhcmUgdXNpbmcgdGhlIGF1dGhlbnRpY2F0aW9uCnRva2VuIHRoYXQgd2Ugc3RvcmVkIGxvY2FsbHkgaW4gdGhlIHByZXZpb3VzIHN0ZXAuCmBgYHtyfQpyZXF1aXJlKFNvY2lhbE1lZGlhTGFiKQpnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrIDwtIAogIExvYWRDcmVkZW50aWFsKCJGQkNyZWRlbnRpYWwuUkRTIikgJT4lCiAgQ29sbGVjdChwYWdlTmFtZT0iU3RhclRyZWsiLCByYW5nZUZyb209IjIwMTUtMDMtMDEiLCByYW5nZVRvPSIyMDE1LTAzLTAyIiwgd3JpdGVUb0ZpbGU9VFJVRSkgJT4lCiAgQ3JlYXRlKCJCaW1vZGFsIikKYGBgCgpSZWFkIGluIHRoZSBkYXRhIHRvIGEgZGF0YWZyYW1lCmBgYHtyfQojcmVhZC5jc3YoKSBpcyBub3QgZXF1aXZhbGVudCB0byBDb2xsZWN0KCkKbXlTdGFyVHJla0RhdGEgPC0gaW1wb3J0RGF0YSgiMjAxNS0wMy0wMV90b18yMDE1LTAzLTAyX1N0YXJUcmVrX0ZhY2Vib29rRGF0YS5jc3YiLCAiZmFjZWJvb2siKSAKCiNpZiB5b3Ugd2FudCB0byBjcmVhdGUgbmV0d29yayBmcm9tIGxvY2FsIGRhdGE6CiMgZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlayA8LSBteVN0YXJUcmVrRGF0YSAlPiUgQ3JlYXRlKCJCaW1vZGFsIikKYGBgCgpOb3cgd2UgY2FuIHBlcmZvcm0gc29tZSBhbmFseXNpcyBvbiB0aGUgU3RhciBXYXJzIG5ldHdvcmsuIEZpcnN0bHksIHdlIHdpbGwgcnVuIHRocm91Z2ggc29tZSBlc3NlbnRpYWwKU05BIHRlY2huaXF1ZXMuIEFmdGVyIHRoYXQgd2Ugd2lsbCBkbyBzb21ldGhpbmcgYSBiaXQgZmFuY2llciwgYnkgY29tcGFyaW5nIHdoZXRoZXIgdGhlcmUgYXJlIGdlbmRlcgpkaWZmZXJlbmNlcyBiZXR3ZWVuIFN0YXIgV2FycyBhbmQgU3RhciBUcmVrIG5ldHdvcmtzLgoKV2UgY2FuIGdldCBkZXNjcmlwdGl2ZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbmV0d29yazoKYGBge3J9CmdfYmltb2RhbF9mYWNlYm9va19zdGFyX3dhcnMKYGBgCgpUaGlzIGluZm9ybXMgdXMgdGhhdCB0aGVyZSBhcmUgMTIxOSBub2RlcyBhbmQgMTIxOCBub2RlcyBpbiB0aGUgbmV0d29yayAodGhpcyBtYXkgZGlmZmVyIHNvbWV3aGF0IGZvciB5b3VyCm93biBjb2xsZWN0ZWQgZGF0YSkuIEl0IHRlbGxzIHVzIHRoYXQgb3VyIGdyYXBoIGlzIERpcmVjdGVkLCBOYW1lZCwgdGhlIGVkZ2VzIGFyZSBXZWlnaHRlZCwgYW5kIGl0IGFsc28gaGFzCnRoZSBhZGRpdGlvbmFsIHByb3BlcnR5IG9mIGJlaW5nIGEgQmlwYXJ0aXRlIGdyYXBoLgoKIyMgQ29sbGVjdGluZyBZb3VUdWJlIHZpZGVvIGNvbW1lbnQgZGF0YQoKIyMjIEF1dGhlbnRpY2F0ZQoKV2UgZmlyc3QgZW5zdXJlIHRoYXQgdGhlIFNvY2lhbE1lZGlhTGFiIHBhY2thZ2UgaXMgbG9hZGVkOgpgYGB7cn0KcmVxdWlyZShTb2NpYWxNZWRpYUxhYikKYGBgCgoKQSBHb29nbGUgRGV2ZWxvcGVyIEFQSSBLZXkgaXMgcmVxdWlyZWQgZm9yIGF1dGhlbnRpY2F0aW5nIHdpdGggdGhlIEFQSSAob3RoZXJ3aXNlIHdlIGNhbm5vdCBjb2xsZWN0IGRhdGEpLiBUaGlzIHJlcXVpcmVzIGEgR29vZ2xlIGFjY291bnQuIEluc3RydWN0aW9ucyBmb3Igb2J0YWluaW5nIGEgR29vZ2xlIERldmVsb3BlciBBUEkgS2V5IGFyZSBhdmFpbGFibGUgZnJvbSB0aGUgW1lvdXR1YmUgRGF0YSBBUEkgT3ZlcnZpZXddKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3lvdXR1YmUvdjMvZ2V0dGluZy1zdGFydGVkKS4gTWFpbiBzdGVwczoKCjEuIFlvdSBuZWVkIGEgR29vZ2xlIEFjY291bnQgdG8gYWNjZXNzIHRoZSBHb29nbGUgRGV2ZWxvcGVycyBDb25zb2xlLCByZXF1ZXN0IGFuIEFQSSBrZXksIGFuZCByZWdpc3RlciB5b3VyIGFwcGxpY2F0aW9uLgoyLiBDcmVhdGUgYSBwcm9qZWN0IGluIHRoZSBHb29nbGUgRGV2ZWxvcGVycyBDb25zb2xlIGFuZCBvYnRhaW4gYXV0aG9yaXphdGlvbiBjcmVkZW50aWFscyBzbyB5b3VyIGFwcGxpY2F0aW9uIGNhbiBzdWJtaXQgQVBJIHJlcXVlc3RzLgozLiBBZnRlciBjcmVhdGluZyB5b3VyIHByb2plY3QsIG1ha2Ugc3VyZSB0aGUgWW91VHViZSBEYXRhIEFQSSBpcyBvbmUgb2YgdGhlIHNlcnZpY2VzIHRoYXQgeW91ciBhcHBsaWNhdGlvbiBpcyByZWdpc3RlcmVkIHRvIHVzZQoKYGBge3J9Cm15X2FwaUtleVlvdXR1YmUgPC0gIkFJemFTeUFPUzZ6OGtRakhKVzV4TDFrbWp0OHRZWmRfSm56aHBGRSIKYGBgCgpXZSB0aGVuIHJ1biB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uLCB3aGljaCBlbnN1cmVzIGV2ZXJ5dGhpbmcgaXMgY29ycmVjdGx5IHNldCB1cCB0byBhY2Nlc3MgdGhlIEFQSToKYGBge3J9CmFwaUtleVlvdXR1YmUgPC0gQXV0aGVudGljYXRlV2l0aFlvdXR1YmVBUEkobXlfYXBpS2V5WW91dHViZSkKYGBgCgojIyMgR2V0IGRhdGEKCldlIG5vdyBhc3NpZ24gYSBjaGFyYWN0ZXIgdmVjdG9yLCBzcGVjaWZ5aW5nIG9uZSBvciBtb3JlIFlvdVR1YmUgdmlkZW8gSURzIHRoYXQgd2Ugd2lzaCB0byBjb2xsZWN0IGRhdGEgZnJvbS4gRm9yIGV4YW1wbGUsIGlmIHRoZSB2aWRlbyBVUkwgaXMgaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1XMkdaRmVZR1UzcywgdGhlbiB1c2UgdmlkZW9JRHMgPSAnVzJHWkZlWUdVM3MnLiBUaXA6IGZvciBtYW55IHZpZGVvcywgdGhlIGZ1bmN0aW9uIEdldFlvdXR1YmVWaWRlb0lEcyBjYW4gYmUgdXNlZCB0byBjcmVhdGUgYSB2ZWN0b3Igb2JqZWN0IHN1aXRhYmxlIGFzIGlucHV0IGZvciB2aWRlb0lEcy4KYGBge3J9CnZpZGVvSURzIDwtIGMoIlcyR1pGZVlHVTNzIiwibUwyN1RBSkdsV2MiKQpgYGAKCldlIG5vdyBjb2xsZWN0IHRoZSBZb3VUdWJlIGNvbW1lbnQgZGF0YSBhbmQgc3RvcmUgaXQgaW4gYSBkYXRhIGZyYW1lIG9iamVjdCBuYW1lZCBteVlvdXR1YmVEYXRhLiBUaGlzIGRhdGEgZnJhbWUgY2FuIHRoZW4gYmUgdXNlZCBmb3IgY3JlYXRpbmcgbmV0d29ya3MgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIFdlIGNhbiBzdXBwbHkgdmFyaW91cyBhcmd1bWVudHMgdG8gdGhlIENvbGxlY3REYXRhWW91dHViZSBmdW5jdGlvbiwgcHJvdmlkaW5nIHZhcmlvdXMgb3B0aW9ucyBmb3IgdGhlIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzLgpgYGB7ciBjYWNoZT1UUlVFfQpteVlvdXR1YmVEYXRhIDwtIENvbGxlY3REYXRhWW91dHViZSh2aWRlb0lEcywgYXBpS2V5WW91dHViZSwgd3JpdGVUb0ZpbGU9VFJVRSwgdmVyYm9zZT1UUlVFLCBtYXhDb21tZW50cz0xMDApCmBgYAoKV2UgY2FuIGV4YW1pbmUgdGhlIHN0cnVjdHVyZSBvZiBvdXIgZGF0YSBmcmFtZToKYGBge3J9CnN0cihteVlvdXR1YmVEYXRhKQpgYGAKCiMjIyBHZW5lcmF0ZSBhY3RvciBuZXR3b3JrCgpXZSB3aWxsIG5vdyBjcmVhdGUgYSB1bmltb2RhbCBuZXR3b3JrLCBhLmsuYSAnYWN0b3IgbmV0d29yaycsIHJlcHJlc2VudGluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gdXNlcnMgd2hvIGhhdmUgaW50ZXJhY3RlZCB3aXRoIGVhY2ggb3RoZXIuIEZvciBZb3VUdWJlIGNvbW1lbnQgdGhyZWFkcywgYSByZWxhdGlvbnNoaXAgaXMgZGVmaW5lZCBhcyB1c2VyIGkgJ3JlcGx5aW5nIHRvJyBvciAnbWVudGlvbmluZycgdXNlciBqIGluIGEgY29tbWVudC4gSW4gdGhpcyBuZXR3b3JrIHRoZSB2ZXJ0aWNlcyAoYS5rLmEgJ25vZGVzJykgcmVwcmVzZW50IFlvdVR1YmUgdXNlcnMgYW5kIHRoZSBlZGdlcyAoYS5rLmEgJ2xpbmtzJykgcmVwcmVzZW50IHdoZXRoZXIgKGFuZCBob3cgbWFueSB0aW1lcykgdXNlciBpIGhhcyBpbnRlcmFjdGVkIHdpdGggdXNlciBqLiBUaGUgZWRnZXMgaW4gdGhpcyBuZXR3b3JrIGFyZSBib3RoIGRpcmVjdGVkIGFuZCB3ZWlnaHRlZC4gRWRnZXMgYXJlICdkaXJlY3RlZCcgYmVjYXVzZSBpbnRlcmFjdGlvbnMgbWF5IG5vdCBiZSByZWNpcHJvY2F0ZWQgKGUuZy4gdXNlciBpIHJlcGxpZXMgdG8gdXNlciBqLCBidXQgdXNlciBqIGRvZXMgbm90IHJlcGx5IHRvIHVzZXIgaSksIGFuZCBlZGdlcyBhcmUgYWxzbyDigJh3ZWlnaHRlZOKAmSBpbiBvcmRlciB0byBzaG93IGhvdyBtYW55IHRpbWVzIHVzZXIgaSBoYXMgaW50ZXJhY3RlZCB3aXRoIHVzZXIgai4KYGBge3J9CnJlcXVpcmUoaWdyYXBoKQpnX2FjdG9yX3lvdXR1YmUgPC0gbXlZb3V0dWJlRGF0YSAlPiUgQ3JlYXRlKCJBY3RvciIpCmBgYAoKV2UgY2FuIG5vdyB2aWV3IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IG91ciBuZXR3b3JrLCBub3RhYmx5IHRoZSBudW1iZXIgb2YgdmVydGljZXMgKHVzZXJzKSBhbmQgbnVtYmVyIG9mIGludGVyYWN0aW9ucyAoZWRnZXMpOgpgYGB7cn0KZ19hY3Rvcl95b3V0dWJlCmBgYAoKCiMjIFN0b3JpbmcgeW91ciBzb2NpYWwgbmV0d29yayBpbiBncmFwaG1sIGZvcm1hdAoKWW91IGNhbiBleHBvcnQgYSBzb2NpYWwgbmV0d29yayBnZW5lcmF0ZWQgYnkgU29jaWFsTWVkaWFMYWIgdG8gImdyYXBobWwiIGZvcm1hdCB1c2luZyBpZ3JhcGggcGFja2FnZToKCmBgYHtyfQpyZXF1aXJlKGlncmFwaCkgIyBpbnN0YWxsLnBhY2thZ2VzKCJpZ3JhcGgiKSBpZiBub3QgaW5zdGFsbGVkIHlldApgYGAKCk5vdyB5b3UgY2FuIHZpc3VhbGl6ZSB0aGUgImdyYXBobWwiIGZpbGUgd2l0aCBHZWxwaGkgb3Igb3RoZXIgY29tcGF0aWFibGUgdG9vbHMuCmBgYHtyfQp3cml0ZS5ncmFwaChnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrLCAiZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlay5ncmFwaG1sIiwgZm9ybWF0PSJncmFwaG1sIikKYGBgCgojIFNvY2lhbCBOZXR3b3JrIEFuYWx5c2lzCgojIyBCYXNpYyBjb3VudGluZwoKTmV4dCB3ZSB3aWxsIGRvIHNvbWUgbW9yZSBkZXNjcmlwdGl2ZSBhbmFseXNpcy4KCkhvdyBtYW55IG5vZGVzIGFyZSBpbiB0aGUgbmV0d29yaz8KYGBge3J9CnZjb3VudChnX2JpbW9kYWxfdHdpdHRlcikKYGBgCgpIb3cgbWFueSBlZGdlcyBpbiB0aGUgbmV0d29yaz8KYGBge3J9CmVjb3VudChnX2JpbW9kYWxfdHdpdHRlcikKYGBgCgpHZXQgYSBsaXN0IG9mIHRoZSBub2RlcyBpbiB0aGUgbmV0d29yazoKYGBge3J9ClYoZ19iaW1vZGFsX3R3aXR0ZXIpCmBgYAoKTGlzdCBvZiBlZGdlcyBpbiB0aGUgbmV0d29yazoKYGBge3J9CkUoZ19iaW1vZGFsX3R3aXR0ZXIpCmBgYAoKQWNjZXNzIGEgcGFydGljdWxhciBub2RlIGluIHRoZSBuZXR3b3JrIChub2RlICM0Mik6CmBgYHtyfQpWKGdfYmltb2RhbF90d2l0dGVyKVs0Ml0KYGBgCgpBY2Nlc3MgYSBwYXJ0aWN1bGFyIGVkZ2VzOgpgYGB7cn0KRShnX2JpbW9kYWxfdHdpdHRlcilbMV0KYGBgCgojIyBHcmFwaCBjb25uZWN0aXZpdHkKCkxvb2sgYXQgdGhlIGNvbm5lY3Rpdml0eSBvZiB0aGUgZ3JhcGg6CmBgYHtyfQojIHdobyBhcmUgdGhlIG5laWdoYm91cnMgb2Ygbm9kZSAjNDI/Cm5laWdoYm9ycyhnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzLDQyKQojdGhpcyBpcyBub3QgYSB3ZWFrbHkgY29ubmVjdGVkIGNvbXBvbmVudAppcy5jb25uZWN0ZWQoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfd2FycywgbW9kZT0id2VhayIpCiNpbmZvcm1hdGlvbiBvbiBjb25uZWN0ZWQgY29tcG9uZW50cwpjYyA8LSBjbHVzdGVycyhnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzKQojd2hpY2ggY29tcG9uZW50IG5vZGUgaXMgYXNzaWduZWQgdG8KIyBjYyRtZW1iZXJzaGlwCiNzaXplIG9mIGVhY2ggY29tcG9uZW50CmNjJGNzaXplCiNudW1iZXIgb2YgY29tcG9uZW50cwpjYyRubwoKI3N1Ym5ldHdvcmsgLSBnaWFudCBjb21wb25lbnQKZzMgPC0gaW5kdWNlZF9zdWJncmFwaChnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzLCB3aGljaChjYyRtZW1iZXJzaGlwID09IHdoaWNoLm1heChjYyRjc2l6ZSkpKQpgYGAKCldlIHdpbGwgbm93IGxvb2sgYXQgbm9kZSBjZW50cmFsaXR5OgpgYGB7ciBldmFsPUZBTFNFfQojbm9kZSBpbmRlZ3JlZQpkZWdyZWUoZzMsIG1vZGU9ImluIikKI25vZGUgb3V0ZGVncmVlCmRlZ3JlZShnMywgbW9kZT0ib3V0IikKI25vZGUgaW5kZWdyZWUsIHVzaW5nIGVkZ2Ugd2VpZ2h0cwppbmQgPC0gc3RyZW5ndGgoZzMsIG1vZGU9ImluIikKI3RvcC01IG5vZGVzLCBiYXNlZCBvbiAod2VpZ2h0ZWQpIGluZGVncmVlClYoZzMpW29yZGVyKGluZCwgZGVjcmVhc2luZz1UKVsxOjNdXQojY2xvc2VuZXNzIGNlbnRyYWxpdHkKY2xvc2VuZXNzKGczKQojYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpiZXR3ZWVubmVzcyhnMykKI2VpZ2VudmVjdG9yIGNlbnRyYWxpdHkKZXZjZW50KGczKSR2ZWN0b3IKYGBgCgoKV2UgY2FuIGxvb2sgYXQgc29tZSBuZXR3b3JrIGNvaGVzaW9uIG1lYXN1cmVzLiBIb3cgZGVuc2UgaXMgdGhlIGdyYXBoPyBJbiBvdGhlciB3b3Jkcywgb2YgYWxsIHRoZSBwb3NzaWJsZSBjb25uZWN0aW9ucyBiZXR3ZWVuIG5vZGVzLCBob3cgbWFueSBhcmUgYWN0dWFsbHkKb2JzZXJ2ZWQ/CmBgYHtyfQojIGRlbnNpdHkKZ3JhcGguZGVuc2l0eShnMykKCiMgKGdsb2JhbCkgY2x1c3RlcmluZyBjb2VmZmljaWVudAojIHJlbC4gZnJlcXVlbmN5IGNvbm5lY3RlZCB0cmlwbGVzIGNsb3NlIHRvIGZvcm0gdHJpYW5nbGVzCnRyYW5zaXRpdml0eShnMykKCiMgbnVtYmVyIG9mIGR5YWRzIHdpdGggcmVjaXByb2NhdGVkIChtdXR1YWwpCiMgZWRnZXMvbnVtYmVyIG9mIGR5YWRzIHdpdGggc2luZ2xlIGVkZ2UKcmVjaXByb2NpdHkoZzMsIG1vZGU9ImRlZmF1bHQiKQoKI3RvdGFsIG51bWJlciBvZiByZWNpcHJvY2F0ZWQgZWRnZXMvdG90YWwgbnVtYmVyIG9mIGVkZ2VzCnJlY2lwcm9jaXR5KGczLCBtb2RlPSJyYXRpbyIpCmBgYAoKIyMgRmluZCBpbXBvcnRhbnQgbm9kZXMgaW4gdGhlIG5ldHdvcmsKCldobyBhcmUgdGhlIHRvcCAzIG1vc3QgaW1wb3J0YW50IHBvc3RzIGluIHRoZSBGYWNlYm9vayBuZXR3b3JrPyBUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGRvIHRoaXMuIEZvciBmdW4gd2Ugd2lsbAp1c2UgdGhlIFBhZ2VSYW5rIGFsZ29yaXRobSBpbXBsZW1lbnRhdGlvbiBpbiBpZ3JhcGggdG8gY2FsY3VsYXRlIHRoaXMuIFBhZ2VSYW5rIGlzIG1hZGUgZmFtb3VzIGJ5IHRoZQpHb29nbGUgY28tZm91bmRlcnMsIHdobyBpbnZlbnRlZCB0aGlzIG1ldGhvZCB0byBkZXRlcm1pbmUgdGhlIGltcG9ydGFuY2Ugb2Ygd2VicGFnZXMsIHJldm9sdXRpb25pc2luZyB0aGUKc2VhcmNoIGVuZ2luZSBpbmR1c3RyeS4gVGhlIGZvbGxvd2luZyBjb2RlIGNhbGN1bGF0ZXMgUGFnZVJhbmsgZm9yIG5vZGVzIGluIHRoZSBuZXR3b3JrLCBhbmQgcmV0dXJucyB0aGUgMwondG9wJyBub2RlcyAod2hpY2ggaGF2ZSB0aGUgaGlnaGVzdCBzaGFyZSBvZiBQYWdlUmFuayksIHByb3ZpZGluZyB0aGUgSUQuCmBgYHtyfQpwYWdlcmFua19pbnN0YWdyYW0gPC0gc29ydChwYWdlLnJhbmsoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfd2FycykkdmVjdG9yLGRlY3JlYXNpbmc9VFJVRSkKaGVhZChwYWdlcmFua19pbnN0YWdyYW0sbj0zKQpgYGAKCldoYXQgYXJlIHRoZSB0b3AgMTAgaW1wb3J0YW50IHRlcm1zIGluIG91ciAjYXVzcG9sIGFjdG9yIG5ldHdvcms/IFRoZXJlIGlzIG5vIHJlYXNvbiB3aHkgd2UgY2FuJ3QgdXNlCnRoZSBQYWdlUmFuayBhbGdvcml0aG0gdG8gY2FsY3VsYXRlIHRoaXMgKGFzIHBlciB0aGUgSW5zdGFncmFtIGFuYWx5c2lzIHByZXZpb3VzbHkpOgpgYGB7cn0KcGFnZVJhbmtfYXVzcG9sX3NlbWFudGljIDwtIHNvcnQocGFnZS5yYW5rKGdfdHdpdHRlcl9zZW1hbnRpYykkdmVjdG9yLGRlY3JlYXNpbmc9VFJVRSkKaGVhZChwYWdlUmFua19hdXNwb2xfc2VtYW50aWMsbj0xMCkKYGBgCgpXaGF0IGFib3V0IHRoZSAzIGxlYXN0IGltcG9ydGFudCB1c2VycyAod2l0aCBhbGwgZHVlIHJlc3BlY3QuLi4pOgpgYGB7cn0KdGFpbChwYWdlUmFua19hdXNwb2xfc2VtYW50aWMsbj0zKQpgYGAKCk9idmlvdXNseSB0aGUgI2F1c3BvbCBoYXNodGFnIGlzIGdvaW5nIHRvIGJlIHRoZSBtb3N0IGltcG9ydGFudCBiZWNhdXNlIGl0IG9jY3VycyBhdCBsZWFzdCBvbmNlIGluIGV2ZXJ5CnR3ZWV0LiBXZSBjYW4gYWN0dWFsbHkgYXZvaWQgdGhpcyBieSB1c2luZyB0aGUgcmVtb3ZlVGVybXNPckhhc2h0YWdzIGFyZ3VtZW50IHdoZW4gd2UgQ3JlYXRlKCkgdGhlCm5ldHdvcmsuIFRoaXMgYXJndW1lbnQgc3BlY2lmaWVzIHdoaWNoIHRlcm1zIG9yIGhhc2h0YWdzIChpLmUuIG5vZGVzIHdpdGggYSBuYW1lIHRoYXQgbWF0Y2hlcyBvbmUgb3IgbW9yZQp0ZXJtcykgc2hvdWxkIGJlIHJlbW92ZWQgZnJvbSB0aGUgc2VtYW50aWMgbmV0d29yay4gVGhpcyBpcyB1c2VmdWwgdG8gcmVtb3ZlIHRoZSBzZWFyY2ggdGVybSBvciBoYXNodGFnCnRoYXQgd2FzIHVzZWQgdG8gY29sbGVjdCB0aGUgZGF0YSAoaS5lLiByZW1vdmUgdGhlIGNvcnJlc3BvbmRpbmcgbm9kZSBpbiB0aGUgZ3JhcGgpLiBGb3IgZXhhbXBsZSwgYSB2YWx1ZSBvZgoiI2F1c3BvbCIgbWVhbnMgdGhhdCB0aGUgbm9kZSB3aXRoIHRoZSBuYW1lICIjYXVzcG9sIiB3aWxsIGJlIHJlbW92ZWQuIE5vdGU6IHlvdSBjb3VsZCBhbHNvIGp1c3QgZGVsZXRlCnRoZSAjYXVzcG9sIG5vZGUgbWFudWFsbHkuCgpBbm90aGVyIGtleSBhc3BlY3Qgb2Ygc2VtYW50aWMgbmV0d29ya3MgaXMgaG93IG1hbnkgdGVybXMgdG8gaW5jbHVkZSBpbiB0aGUgbmV0d29yay4gQnkgZGVmYXVsdCwKU29jaWFsTWVkaWFMYWIgZG9lcyBub3QgaW5jbHVkZSBldmVyeSB1bmlxdWUgdGVybSB0aGF0IGl0IGZpbmRzIGluIHRoZSB0d2VldHMsIGJ1dCBvbmx5IHRoZSA1IHBlcmNlbnQgbW9zdApmcmVxdWVudGx5IG9jY3VycmluZyB0ZXJtcy4gWW91IGNhbiBjaGFuZ2UgdGhpcyB3aGVuIGNhbGxpbmcgdGhlIENyZWF0ZSgpIG5ldHdvcmsgZnVuY3Rpb24sIGZvciBleGFtcGxlIGJ5CnNwZWNpZnlpbmcgYSB2YWx1ZSA1MCAobWVhbmluZyB0aGF0IHRoZSA1MCBwZXJjZW50IG1vc3QgZnJlcXVlbnRseSBvY2N1cnJpbmcgdGVybXMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUKc2VtYW50aWMgbmV0d29yaykuCgpXZSBjYW4gYWN0dWFsbHkgdHJ5IHRoaXMgb3V0IG5vdy4gV2Ugd2lsbCBjcmVhdGUgYW5vdGhlciBzZW1hbnRpYyBuZXR3b3JrLCBidXQgd2Ugd2lsbCBleGNsdWRlIHRoZSAjYXVzcG9sCmhhc2h0YWcsIGFuZCB3ZSB3aWxsIGluY2x1ZGUgZXZlcnkgc2luZ2xlIHRlcm0gYXZhaWxhYmxlIGluIHRoZSB0d2VldHMuCmBgYHtyfQpnX3R3aXR0ZXJfc2VtYW50aWNfYXVzcG9sX2FsbFRlcm1zIDwtIG15VHdpdHRlckRhdGEgJT4lIENyZWF0ZSgiU2VtYW50aWMiLCB0ZXJtRnJlcT0xMDAsIHJlbW92ZVRlcm1zT3JIYXNodGFncz1jKCIjYXVzcG9sIikpCmBgYAoKVGhlIHNpemUgb2YgdGhlIG5ldHdvcmsgd2lsbCBpbmNyZWFzZSBhIGxvdCwgZXZlbiBpbiB0aGUgYWJzZW5jZSBvZiB0aGUgI2F1c3BvbCB0ZXJtIQpgYGB7cn0KZ190d2l0dGVyX3NlbWFudGljX2F1c3BvbF9hbGxUZXJtcwpgYGAKCldoYXQgYXJlIHRoZSB0b3AgMTAgaW1wb3J0YW50IHRlcm1zIGluIG91ciBzZW1hbnRpYyBuZXR3b3JrIG5vdz8gT25jZSBhZ2FpbiB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGlzIHVzaW5nIFBhZ2VSYW5rOgpgYGB7cn0KcGFnZVJhbmtfYXVzcG9sX3NlbWFudGljX3JlcGxpY2F0ZSA8LSBzb3J0KHBhZ2UucmFuayhnX3R3aXR0ZXJfc2VtYW50aWNfYXVzcG9sX2FsbFRlcm1zKSR2ZWN0b3IsZGVjcmVhc2luZz1UUlVFKQpwYWdlUmFua19hdXNwb2xfc2VtYW50aWNfcmVwbGljYXRlWzE6MTBdCmBgYAoKCiMjIEZpbmQgY29tbXVuaXRpZXMKCklzIHRoZXJlIGFueSBraW5kIG9mIGNvbW11bml0eSBzdHJ1Y3R1cmUgd2l0aGluIHRoZSB1c2VyIG5ldHdvcms/IFdlIHdpbGwgdXNlIHRoZSBpbmZvbWFwIGFsZ29yaXRobSBpbXBsZW1lbnRhdGlvbiBpbiBpZ3JhcGguCmBgYHtyIHRpZHk9VFJVRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShpZ3JhcGgpCiMgaW5jcmVhc2UgbmIudHJpYWxzIGZvciBiZXR0ZXIgcXVhbGl0eSBjb21tdW5pdGllcwppbWMgPC0gaW5mb21hcC5jb21tdW5pdHkoZ190d2l0dGVyX2FjdG9yLCBuYi50cmlhbHMgPSAxMCkgCgojIGNyZWF0ZSBhIHZlY3RvciBvZiB1c2VycyB3aXRoIHRoZWlyIGFzc2lnbmVkIGNvbW11bml0eSBudW1iZXIKY29tbXVuaXR5TWVtYmVyc2hpcF9hdXNwb2wgPC0gbWVtYmVyc2hpcChpbWMpCgojIHN1bW1hcmlzZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHVzZXJzIHRvIGNvbW11bml0aWVzIApjb21tRGlzdHJpYnV0aW9uIDwtIHN1bW1hcnkoYXMuZmFjdG9yKGNvbW11bml0eU1lbWJlcnNoaXBfYXVzcG9sKSkgCgojIHdoaWNoIGNvbW11bml0eSBoYXMgdGhlIG1heCBudW1iZXIgb2YgdXNlcnMgCnRhaWwoc29ydChjb21tRGlzdHJpYnV0aW9uKSxuPTEpCmBgYAoKTG9vayBpbnRvIHRoZSBtZW1iZXJzIG9mIGVhY2ggY29tbXVuaXR5OgpgYGB7cn0KIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW11bml0aWVzIHRoYXQgaW5jbHVkZXMgdGhlIHVzZXJzIGFzc2lnbmVkIHRvIGVhY2ggY29tbXVuaXR5CmNvbW11bml0aWVzX2F1c3BvbCA8LSBjb21tdW5pdGllcyhpbWMpCgojIGxvb2sgYXQgdGhlIG1lbWJlcnMgb2YgdGhlIG1vc3QgcG9wdWxhdGVkIGNvbW11bml0eSAKY29tbXVuaXRpZXNfYXVzcG9sW25hbWVzKHRhaWwoc29ydChjb21tRGlzdHJpYnV0aW9uKSxuPTEpKV0KYGBgCgojIyBHcmFwaCBQcm9qZWN0aW9uCgpBbm90aGVyIHVzZWZ1bCB0ZWNobmlxdWUgd2UgY2FuIGRvIGlzIHRvIHBlcmZvcm0gYSBwcm9qZWN0aW9uIG9mIHRoZSBGYWNlYm9vayBuZXR3b3JrcyB3ZSBqdXN0IGNyZWF0ZWQuIFRoZXNlIG5ldHdvcmtzIGFyZSBiaXBhcnRpdGUgYmVjYXVzZSBub2RlcyBvZiB0aGUgc2FtZSB0eXBlIGNhbm5vdCBzaGFyZSBhbiBlZGdlIChlLmcuIGEgdXNlciBjYW4gb25seSBsaWtlL2NvbW1lbnQgb24gYSBwb3N0LCBidXQgbm90IGxpa2UvY29tbWVudCBhbm90aGVyIHVzZXIsIGFuZCBwb3N0cyBjYW5ub3QgcGVyZm9ybSBkaXJlY3RlZCBhY3Rpb25zIGVpdGhlciBvbiB1c2VycyBvciBvdGhlciBwb3N0cykuCgpXaGF0IHdlIGNhbiBkbyBpcyBpbmR1Y2UgdHdvIHN1YmdyYXBocyBmcm9tIGVhY2ggbmV0d29yay4gTW9yZSBzcGVjaWZpY2FsbHksIHdlIGNhbiBpbmR1Y2UgdHdvIGFjdG9yIG5ldHdvcmtzLCBvbmUgZm9yIHRoZSB1c2VycyBhbmQgb25lIGZvciB0aGUgcG9zdHMuCmBgYHtyfQojIyBzb21lIGRhdGEgcHJlcGFyYXRpb24KIyBjb2VyY2UgdG8gZmFjdG9yCmdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbiA8LSBnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrClYoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKSR0eXBlIDwtIGFzLmZhY3RvcihWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbikkdHlwZSkKCiMgY29lcmNlIGFsbCBwb3N0cyAoaS5lLiAiMSIpIHRvIGxvZ2ljYWwgKGkuZS4gRkFMU0UpClYoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKSR0eXBlW3doaWNoKFYoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKSR0eXBlPT0iMSIpXSA8LSBhcy5sb2dpY2FsKEZBTFNFKQoKIyBjb2VyY2UgYWxsIHVzZXJzIChpLmUuICIyIikgdG8gbG9naWNhbCAoaS5lLiBUUlVFKQpWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbikkdHlwZVt3aGljaChWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbikkdHlwZT09IjIiKV0gPC0gYXMubG9naWNhbChUUlVFKQoKIyBub3cgcHJvamVjdCB0aGUgbmV0d29yawpwcm9qZWN0aW9uX2dfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWsgPC0gYmlwYXJ0aXRlLnByb2plY3Rpb24oZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKQpgYGAKCkZpcnN0bHksIHdlIHdpbGwgbG9vayBhdCB0aGUgaW5kdWNlZCBncmFwaCBmb3IgdGhlICJwb3N0cyIuIFRoZSBpbmR1Y2VkICJwb3N0cyIiIGFjdG9yIG5ldHdvcmsgY29uc2lzdHMgb25seSBvZiBub2RlcyB0aGF0IGFyZSBvZiB0eXBlICJwb3N0Ii4gQW4gZWRnZSBleGlzdHMgYmV0d2VlbiBwb3N0IGkgYW5kIHBvc3QgaiBpZiB0aGV5IGFyZSBib3RoIGNvLWxpa2VkIG9yIGNvLWNvbW1lbnRlZCBieSB0aGUgc2FtZSB1c2VyIChpLmUuIGlmIHRoZXkgaGF2ZSBhbnkgdXNlciBpbiBjb21tb24pLiBOb3Qgc3VycHJpc2luZ2x5LCBldmVyeSBwb3N0IGhhcyBhdCBsZWFzdCBvbmUgdXNlciBpbiBjb21tb24sIHdoaWNoIHJlc3VsdHMgaW4gdGhlIG5ldHdvcmsgYmVpbmcgImNvbXBsZXRlIi4KYGBge3IgdGlkeT1UUlVFfQpwcm9qZWN0aW9uX2dfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtbWzFdXQojIHBuZygiZmFjZWJvb2tfc3Rhcl90cmVrX3Bvc3RzLnBuZyIsIHdpZHRoPTgwMCwgaGVpZ2h0PTcwMCkgCnBsb3QocHJvamVjdGlvbl9nX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrW1sxXV0sIGVkZ2Uud2lkdGg9MS41LCBlZGdlLmN1cnZlZD0wLjUsIGVkZ2UuYXJyb3cuc2l6ZT0wLjUpICN2ZXJ0ZXguc2hhcGU9Im5vbmUiLAojIGRldi5vZmYoKQpgYGAKClNlY29uZGx5LCB3ZSB3aWxsIGxvb2sgYXQgdGhlIGluZHVjZWQgZ3JhcGggZm9yIHRoZSDigJx1c2Vyc+KAnS4gVGhlIGluZHVjZWQg4oCcdXNlcnPigJ0gYWN0b3IgbmV0d29yayBjb25zaXN0cyBvbmx5IG9mIG5vZGVzIHRoYXQgYXJlIG9mIHR5cGUg4oCcdXNlcuKAnS4gQW4gZWRnZSBleGlzdHMgYmV0d2VlbiB1c2VyIGkgYW5kIHVzZXIgaiBpZiB0aGV5IGJvdGggY28tbGlrZWQgb3IgY28tY29tbWVudGVkIHRoZSBzYW1lIHBvc3QgKGkuZS4gdGhleSBzaGFyZSBhbiBpbnRlcmFjdGlvbiB3aXRoIGEgcG9zdCBqKS4gQXMgeW91IG1pZ2h0IGV4cGVjdCwgdGhpcyBjcmVhdGUgYSBuZXR3b3JrIHdpdGggYSBtYXNzaXZlIG51bWJlciBvZiBlZGdlcyEgQSBsb3Qgb2YgdXNlcnMgY28taW50ZXJhY3Qgd2l0aCB0aGUgc2FtZSBwb3N0cy4gRm9yIHRoaXMgZXhhbXBsZSwgb3ZlciA0LjUgbWlsbGlvbiBlZGdlcyAoeW91ciByZXN1bHRzIG1pZ2h0IGJlIHNvbWV3aGF0IGRpZmZlcmVudCkuCmBgYHtyfQojIHdhcm5pbmcgLSBkbyBub3QgdXNlIOKAmHN0cuKAmCBmdW5jdGlvbiBiZWNhdXNlIGl0IHdpbGwKIyBjYXVzZSBSIHRvIGZyZWV6ZSB1cCBkdWUgdG8gb3ZlcmxvYWRpbmcgdGhlIGNvbnNvbGUgb3V0cHV0IQojIEFsc286IHlvdSB3aWxsIHByb2JhYmx5IGhhdmUgZGlmZmljdWx0eSBwbG90dGluZyB0aGlzIGdyYXBoIGluIFIgYmVjYXVzZSBpdCBpcyBzbyBiaWcgCnByb2plY3Rpb25fZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla1tbMl1dCmBgYAoKTWF5YmUgdGhlcmUgaXMgc29tZSBjb21tdW5pdHkgc3RydWN0dXJlIHRvIHRoaXMgbGFyZ2UgbmV0d29yay4gVGhlcmUgYXJlIHNldmVyYWwgd2F5cyB0byBmaW5kIG91dC4gV2Ugd2lsbCB1c2UgdGhlIGluZm9tYXAgYWxnb3JpdGhtIGltcGxlbWVudGF0aW9uIGluIGlncmFwaC4gSW5mb21hcCB1c2VzIGFuIGluZm9ybWF0aW9uIHRoZW9yZXRpYywgZmxvdy1iYXNlZCBhcHByb2FjaCB0byBjYWxjdWxhdGluZyBjb21tdW5pdHkgc3RydWN0dXJlIGluIG5ldHdvcmtzLiBJdCBzdXBwb3J0cyB3ZWlnaHRlZCBhbmQgZGlyZWN0ZWQgZ3JhcGhzLCBhbmQgYWxzbyBzY2FsZXMgd2VsbC4KClRoZSByZXN1bHRzIHNob3cgdGhhdCB0aGVyZSBpcyBkZWZpbml0ZWx5IHNvbWUgaW50ZXJlc3RpbmcgY29tbXVuaXR5IHN0cnVjdHVyZSB0byB0aGUgdXNlciBhY3RvciBuZXR3b3JrIChhIGhhbmRmdWwgb2YgbGFyZ2UgY29tbXVuaXRpZXMgYW5kIGEgdGlueSBjb21tdW5pdHkpLiBBbHRob3VnaCB5b3VyIHJlc3VsdHMgbWlnaHQgZGkgZXIsIGRlcGVuZGluZyBvbiB0aGUgYWN0dWFsIGRhdGEgY29sbGVjdGVkLgpgYGB7ciB0aWR5PVRSVUUsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9CiMgbGltaXQgdGhlIOKAmHRyaWFsc+KAmCBhcmd1bWVudCB0byBhIHNtYWxsIG51bWJlciB0byBzYXZlIHRpbWUgCiMgKG51bWJlciBvZiBhdHRlbXB0cyB0byBwYXJ0aXRpb24gdGhlIG5ldHdvcmspIAppbWNfc3RhcndhcnMgPC0gaW5mb21hcC5jb21tdW5pdHkocHJvamVjdGlvbl9nX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrW1syXV0sIG5iLnRyaWFscyA9IDMpCgojIGNyZWF0ZSBhIHZlY3RvciBvZiB1c2VycyB3aXRoIHRoZWlyIGFzc2lnbmVkIGNvbW11bml0eSBudW1iZXIKY29tbXVuaXR5TWVtYmVyc2hpcF9zdGFyd2FycyA8LSBtZW1iZXJzaGlwKGltY19zdGFyd2FycykgCgojIHN1bW1hcmlzZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHVzZXJzIHRvIGNvbW11bml0aWVzIApjb21tRGlzdHJpYnV0aW9uX3N0YXJ3YXJzIDwtIHN1bW1hcnkoYXMuZmFjdG9yKGNvbW11bml0eU1lbWJlcnNoaXBfc3RhcndhcnMpKQoKIyB3aGljaCBjb21tdW5pdHkgaGFzIHRoZSBtYXggbnVtYmVyIG9mIHVzZXJzIAp0YWlsKHNvcnQoY29tbURpc3RyaWJ1dGlvbl9zdGFyd2Fycyksbj0xKQoKIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW11bml0aWVzIHRoYXQgaW5jbHVkZXMgdGhlIHVzZXJzIGFzc2lnbmVkIHRvIGVhY2ggY29tbXVuaXR5CmNvbW11bml0aWVzX3N0YXJ3YXJzIDwtIGNvbW11bml0aWVzKGltY19zdGFyd2FycykKCiMgbG9vayBhdCB0aGUgbWVtYmVycyBvZiB0aGUgKmxlYXN0KiBwb3B1bGF0ZWQgY29tbXVuaXR5IApjb21tdW5pdGllc19zdGFyd2Fyc1tuYW1lcyhoZWFkKHNvcnQoY29tbURpc3RyaWJ1dGlvbl9zdGFyd2Fycyksbj0xKSldCmBgYAoKIyBUZXh0IEFuYWx5c2lzCgpOZXh0LCB3ZSB3aWxsIGRvIHNvbWUgZGVzY3JpcHRpdmUgdGV4dCBhbmFseXNpcyBvZiB0aGUgU3RhciBXYXJzIGZhbiBjb21tZW50cy4KClRPRE8KCiMjIERhdGEgcHJlLXByb2Nlc3NpbmcKCldlIGp1c3Qgd2FudCB0byBrZWVwIHRoZSBjaGFyYWN0ZXIgdmVjdG9yIG9mIOKAmGNvbW1lbnRz4oCZIGRhdGEsIGZvciBvdXIgcHVycG9zZXMgaW4gdGhpcyBzZXNzaW9uOgpgYGB7cn0KZmJEYXRhIDwtIG15U3RhcldhcnNEYXRhJGNvbW1lbnRUZXh0CmBgYAoKV2Ugb25seSB3YW50IGVsZW1lbnRzIG9mIGZiRGF0YSB0aGF0IGNvbnRhaW4gY29tbWVudCB0ZXh0IChtYW55IHJvd3Mgb2Ygb3VyIEZhY2Vib29rIGRhdGEgcmVwcmVzZW50CuKAmGxpa2Vz4oCZLCByYXRoZXIgdGhhbiDigJhjb21tZW50c+KAmSkuIFNvIHdlIHJlbW92ZSBhbnkgdGV4dCBkYXRhIHRoYXQgZXF1YWxzIOKAnE5vdF9hcHBsaWNhYmxl4oCdICh0aGlzIGlzIGhvdwpTb2NpYWxNZWRpYUxhYiBkZXNpZ25hdGVzIHJvd3MgaW4gdGhlIGRhdGFmcmFtZSB0aGF0IGFyZSDigJhsaWtlc+KAmSkuIE5vdGU6IGluIGVhcmxpZXIgdmVyc2lvbnMgb2YgU29jaWFsTWVkaWFMYWIKdGhlc2UgZWxlbWVudHMgd2VyZSBkZXNpZ25hdGVkIGFzIE5BLCBob3dldmVyIHRoaXMgY2F1c2VkIHVuaW50ZW5kZWQgY29uc2VxdWVuY2VzIHNvIGl0IHdhcyBjaGFuZ2VkLgpgYGB7cn0KdG9SZW1vdmUgPC0gd2hpY2goZmJEYXRhPT0iTm90X2FwcGxpY2FibGUiKQpmYkRhdGEgPC0gZmJEYXRhWy10b1JlbW92ZV0gIyByZW1vdmUgdGhlIGVsZW1lbnRzIHdlIHdhbnQgdG8gZXhjbHVkZQpgYGAKCkhvdyBtYW55IGNvbW1lbnRzIGRvIHdlIGhhdmUgbGVmdCBub3c/CmBgYHtyfQpsZW5ndGgoZmJEYXRhKQpgYGAKCldlIGNvbnZlcnQgdGhlIGNoYXJhY3RlciBlbmNvZGluZyB0byBVVEYtOC4gVGhpcyBhdm9pZHMgZXJyb3JzIHJlbGF0aW5nIHRvIOKAmG9kZOKAmSBjaGFyYWN0ZXJzIGluIHRoZSB0ZXh0LiBUaGlzCmlzIHVzdWFsbHkgYSBnb29kIGlkZWEsIGJ1dCB0aGVyZSBtYXkgYmUgc2l0dWF0aW9ucyB3aGVuIGl0IGlzIG5vdCB1c2VmdWwsIG9yIGV2ZW4gZGV0cmltZW50YWwuIE5vdGU6IE1hYyB1c2VycwptYXkgZW5jb3VudGVyIGVycm9ycy9idWdzIHJlbGF0aW5nIHRvIGNoYXJhY3RlciBlbmNvZGluZywgYW5kIGEgd29ya2Fyb3VuZCBpcyB0byBjb252ZXJ0IHRvIOKAmHV0Zi04LW1hY+KAmToKYGBge3J9CmZiRGF0YSA8LSBpY29udihmYkRhdGEsIHRvID0gInV0Zi04IikKIyAqKk1BQyBVU0VSUyBPTkxZKiogc2hvdWxkIHVzZSB0aGlzIGluc3RlYWQ6CmZiRGF0YSA8LSBpY29udihmYkRhdGEsdG89InV0Zi04LW1hYyIpCmBgYAoKV2UgY29udmVydCBvdXIgY2hhcmFjdGVyIHZlY3RvciBmYkRhdGEgdG8gYSBWY29ycHVzIG9iamVjdDoKYGBge3Igd2FybmluZz1GQUxTRX0KbGlicmFyeSh0bSkKZmJDb3JwdXMgPC0gVkNvcnB1cyhWZWN0b3JTb3VyY2UoZmJEYXRhKSkKYGBgCgpJbmRpdmlkdWFsIGNvbW1lbnRzIChhLmsuYS4g4oCYZG9jdW1lbnRz4oCZKSBjYW4gYmUgYWNjZXNzZWQgdmlhIHRoZSBkb3VibGUgYnJhY2tldHMgbm90YXRpb24gb3IgdGhlIOKAmGRvbGxhcgpzaWdu4oCZIG5vdGF0aW9uIGZvciBhY2Nlc3NpbmcgbGlzdCBlbGVtZW50cy4gTGV04oCZcyBsb29rIGF0IGNvbW1lbnQgIzQuCmBgYHtyfQpmYkNvcnB1c1tbNF1dW1sxXV0KIyBhbm90aGVyIHdheSB0byBhY2Nlc3MgaXQKZmJDb3JwdXNbWzRdXSRjb250ZW50CmBgYAoKV2UgY2FuIHBlcmZvcm0gYSBudW1iZXIgb2YgaGlnaGx5IHVzZWZ1bCB0cmFuc2Zvcm1hdGlvbnMgb2YgdGV4dCB1c2luZyB0bV9tYXAgZnVuY3Rpb24gKGkuZS4g4oCYbWFwcGluZyB0bwp0aGUgY29ycHVz4oCZKS4gTm90IGFsbCBvZiB0aGVzZSB0cmFuc2Zvcm1hdGlvbnMgYXJlIHVzZWZ1bCBpbiBldmVyeSBzY2VuYXJpbyEgVGhleSBzaG91bGQgYmUgdXNlZCBvbmx5IHdoZW4gaXQKbWFrZXMgc2Vuc2UsIG9yIGFzIHJlcXVpcmVkLCBldGMuCgpDb252ZXJ0aW5nIGFsbCB0aGUgdGV4dCB0byBsb3dlcmNhc2U6CmBgYHtyfQpmYkNvcnB1cyA8LSB0bV9tYXAoZmJDb3JwdXMsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCmBgYAoKUmVtb3ZlIG51bWJlcnMgZnJvbSB0aGUgdGV4dDoKYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlTnVtYmVycykKYGBgCgpSZW1vdmUgcHVuY3R1YXRpb24gZnJvbSB0aGUgdGV4dDoKYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pCmBgYAoKUGVyZm9ybSDigJh3b3JkIHN0ZW1taW5n4oCZIG9uIHRoZSB0ZXh0LiBOb3RlOiB0aGlzIHRyYW5zZm9ybWF0aW9uIGNhbiBiZSBoaWdobHkgdXNlZnVsLCBidXQgYWxzbyBoaWdobHkKZGV0cmltZW50YWwhCmBgYHtyfQojIHVzZSBsYXp5PVRSVUUgYXJndW1lbnQgdG8gYXZvaWQgd2FybmluZyBvbiBzb21lIG1hY2hpbmVzIHdpdGggbXVsdGlwbGUgQ1BVIGNvcmVzCmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgc3RlbURvY3VtZW50LGxhenk9VFJVRSkKYGBgCgpXZSBjYW4gYWxzbyByZW1vdmUgRW5nbGlzaCDigJhzdG9wIHdvcmRz4oCZIGZyb20gdGhlIHRleHQuIFRoZXNlIGFyZSBjb21tb24gd29yZHMgKGUuZy4g4oCYdGhl4oCZLCDigJhhbmTigJksIOKAmG9y4oCZKSB0aGF0CndlIG1heSB3YW50IHRvIGV4Y2x1ZGUgZnJvbSBvdXIgYW5hbHlzaXMuIE9uY2UgYWdhaW4sIHRoaXMgaXMgaGlnaGx5IHVzZWZ1bCBidXQgYWxzbyBuZWVkcyB0byBiZSBjYXJlZnVsbHkKYXBwbGllZC4KYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpLGxhenk9VFJVRSkgCiMgdXNlIGxhenk9VFJVRSBhcmd1bWVudCB0byBhdm9pZCB3YXJuaW5nIG9uIHNvbWUgbWFjaGluZXMgd2l0aCBtdWx0aXBsZSBDUFUgY29yZXMKYGBgCgpFbGltaW5hdGUgdW5uZWNlc3Nhcnkg4oCYd2hpdGUgc3BhY2XigJkgZnJvbSB0aGUgdGV4dC4gRm9yIGV4YW1wbGUsIOKAnGhlbGxvIGV2ZXJ5b25lIG15IG5hbWUgaXMgZnJlZOKAnSBiZWNvbWVzCuKAnGhlbGxvIGV2ZXJ5b25lIG15IG5hbWUgaXMgZnJlZOKAnToKYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgc3RyaXBXaGl0ZXNwYWNlLCBsYXp5PVRSVUUpCmBgYAoKV2UgY2FuIG9ic2VydmUgdGhlIGRpP2VyZW5jZSBub3cgYnkgZXhhbWluaW5nIGNvbW1lbnQgIzQgYWdhaW46CmBgYHtyfQpmYkNvcnB1c1tbNF1dJGNvbnRlbnQKYGBgCgpXZSBjb3VsZCBhbHNvIGRlZmluZSBvdXIgb3duIHN0b3Agd29yZHMgYW5kIHRyYW5zZm9ybSB0aGUgdGV4dCB1c2luZyB0aGVzZToKYGBge3J9Cm15U3RvcHdvcmRzIDwtIGMoImphciIsImJpbmtzIikKZmJDb3JwdXMgPC0gdG1fbWFwKGZiQ29ycHVzLCByZW1vdmVXb3JkcywgbXlTdG9wd29yZHMpCmBgYAoKCgojIyBGcmVxdWVuY3kgYW5hbHlzaXMKCk5leHQgd2UgY3JlYXRlIGEgZG9jdW1lbnQtdGVybSBtYXRyaXggKERUTSkgZnJvbSB0aGUgZmJDb3JwdXMgb2JqZWN0LiBEVE1zIGFyZSBhIHZlcnkgaW1wb3J0YW50CmNvbmNlcHQgZm9yIHRleHQgYW5hbHlzaXMgYW5kIGFyZSBoaWdobHkgdXNlZnVsLiBEVE1zIGNhbiBiZSB0aG91Z2h0IGFib3V0IGFzIGEgdGFibGUgKGkuZS4gbWF0cml4KSB3aGVyZQp0aGUgcm93cyBhcmUg4oCYZG9jdW1lbnRz4oCZIChpLmUuIEZhY2Vib29rIGNvbW1lbnRzIGluIG91ciBkYXRhc2V0KSwgYW5kIHRoZSBjb2x1bW5zIGFyZSDigJh0ZXJtc+KAmSAoaS5lLiBlYWNoCnVuaXF1ZSB3b3JkIGZvdW5kIGFjcm9zcyBhbGwgdGhlIGRvY3VtZW50cyBpbiB0aGUgZGF0YXNldCkuIFRoZSDigJhjZWxsc+KAmSAoaS5lLiBlbGVtZW50cykgb2YgdGhlIG1hdHJpeCBpbmRpY2F0ZQpob3cgbWFueSB0aW1lcyB0ZXJtIG4gb2NjdXJyZWQgaW4gZG9jdW1lbnQgbS4KCk5vdGU6IHdlIHVzZSB0aGUgY29udHJvbCBhcmd1bWVudCB0byBzcGVjaWZ5IHRoYXQgd2Ugb25seSB3YW50IHRvIHJldGFpbiB3b3JkcyB0aGF0IGFyZSBtaW5pbXVtIGNoYXJhY3RlcgpsZW5ndGggb2YgMywgdXAgdG8gYSBtYXhpbXVtIG9mIDIwIGNoYXJhY3RlcnMuCmBgYHtyfQpkdG0gPC0gRG9jdW1lbnRUZXJtTWF0cml4KGZiQ29ycHVzLGNvbnRyb2wgPSBsaXN0KHdvcmRMZW5ndGhzPWMoMywgMjApKSkKZHRtCmBgYAoKV2hhdCB3ZSBoYXZlIGlzIGEgc3BhcnNlIG1hdHJpeCwgaS5lLiBtb3N0IG9mIHRoZSBlbGVtZW50cyBvZiB0aGUgbWF0cml4IGFyZSAwLCBpLmUuIGluIG91ciBkYXRhc2V0IG1vc3QKRmFjZWJvb2sgY29tbWVudHMgY29udGFpbiBvbmx5IGEgc21hbGwgcGVyY2VudGFnZSBvZiDigJh2b2NhYnVsYXJ54oCZIG9mIHRlcm1zIG9ic2VydmVkIGFjcm9zcyB0aGUgZW50aXJlIHNldCBvZgpjb21tZW50cy4gV2hhdCB3ZSB3YW50IHRvIGRvIGlzIHJlbW92ZSB0ZXJtcyB0aGF0IG9jY3VyIHZlcnkgaW5mcmVxdWVudGx5LCB3aGljaCB3aWxsIGxlYXZlIHVzIHdpdGggdGhlCm1vc3Qg4oCYaW1wb3J0YW504oCZIHRlcm1zLiBXZSByZW1vdmUgc3BhcnNlIHRlcm1zIHVzaW5nIHRoZSByZW1vdmVTcGFyc2VUZXJtcyBmdW5jdGlvbiwgd2hpY2ggcmVtb3Zlcwp0ZXJtcyB0aGF0IG9jY3VyIGVxdWFsIHRvIG9yIGxlc3MgdGhhbiBhIHBlcmNlbnRhZ2UgdGhyZXNob2xkLgoKRm9yIGV4YW1wbGUsIGlmIHdlIHNldCBpdCB0byAwLjk5NSwgdGhlbiBhbGwgdGVybXMgdGhhdCBhcmUgYXQgbGVhc3QgOTkuNSUgc3BhcnNlIGFyZSByZW1vdmVkLiBUaGUgZm9sbG93aW5nCmNvbW1hbmQgbGV0cyB1cyDigJh0ZXN0IG91dOKAmSB3aGF0IG91ciBkb2N1bWVudC10ZXJtIG1hdHJpeCB3b3VsZCBsb29rIGxpa2UgaWYgd2Ugc2V0IHRoZSB0aHJlc2hvbGQgdG8gMC45OTU6CmBgYHtyfQpyZW1vdmVTcGFyc2VUZXJtcyhkdG0sIDAuOTk1KQpgYGAKCjAuOTk1IHdpbGwgZG8gdGhlIHRyaWNrIGZvciB1cyBpbiB0aGlzIHdvcmtzaG9wLCBzbyB3ZSB3aWxsIGNyZWF0ZSBhIG5ldyBkb2N1bWVudC10ZXJtIG1hdHJpeCB3aXRoIHRoaXMKdGhyZXNob2xkIGFwcGxpZWQgdG8gaXQ6CmBgYHtyfQpkdG1TcGFyc2VSZW1vdmVkIDwtIHJlbW92ZVNwYXJzZVRlcm1zKGR0bSwgMC45OTUpCmBgYAoKV2UgY2FuIGV4YW1pbmUgdGVybSBmcmVxdWVuY2llcyBpbiBvdXIgZGF0YS4gV2UgY3JlYXRlIGEgY2hhcmFjdGVyIHZlY3RvciBvZiB0aGUgc3VtcyBvZiBjb2x1bW5zIG9mIG91cgpkb2N1bWVudC10ZXJtIG1hdHJpeCAoaW1wbGljaXRseSBjb2VyY2luZyBpdCB0byBhIG1hdHJpeCBvYmplY3QpLCBtZWFuaW5nIHRoYXQgaGF2ZSBhIG5hbWVkIGNoYXJhY3Rlcgp2ZWN0b3Igd2hlcmUgdGhlIG5hbWVzIGFyZSB0aGUgdW5pcXVlIHRlcm1zIGluIG91ciBkb2N1bWVudC10ZXJtIG1hdHJpeCwgYW5kIHRoZSB2YWx1ZXMgb2YgdGhlIGVsZW1lbnRzCmFyZSB0aGUgbnVtYmVyIG9mIHRpbWVzIHRoYXQgcGFydGljdWxhciB3b3JkIG9jY3VycyBhY3Jvc3MgYWxsIG9mIG91ciBjb3JwdXMuCmBgYHtyfQpmcmVxVGVybXMgPC0gY29sU3Vtcyhhcy5tYXRyaXgoZHRtU3BhcnNlUmVtb3ZlZCkpCmZyZXFUZXJtcwpgYGAKCldlIG9yZGVyIHRoZSB0ZXJtIGZyZXF1ZW5jaWVzIGFuZCBsb29rIGF0IHRoZSA1IG1vc3QgZnJlcXVlbnQgdGVybXMgYW5kIHRoZW4gNSBsZWFzdCBmcmVxdWVudCB0ZXJtczoKYGBge3J9Cm9yZGVyVGVybXMgPC0gb3JkZXIoZnJlcVRlcm1zLGRlY3JlYXNpbmc9VFJVRSkKZnJlcVRlcm1zW2hlYWQob3JkZXJUZXJtcyldCmZyZXFUZXJtc1t0YWlsKG9yZGVyVGVybXMpXQpgYGAKCldoaWNoIHRlcm1zIG9jY3VycmVkIGF0IGxlYXN0IDIwIHRpbWVzPwpgYGB7cn0KZmluZEZyZXFUZXJtcyhkdG1TcGFyc2VSZW1vdmVkLCAyMCkKYGBgCgpXZSBjYW4gZG8gYSBiYXNpYyBjb3JyZWxhdGlvbiBhbmFseXNpcyBieSBsb29raW5nIGF0IHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0ZXJtcyB3aXRoIHRoZSBmaW5kQXNzb2NzCmZ1bmN0aW9uLiBJZiB0d28gd29yZHMgYWx3YXlzIGFwcGVhciB0b2dldGhlciB0aGVuIGNvcnIgPSAxLiBJZiB0d28gdGVybXMgbmV2ZXIgYXBwZWFyIHRvZ2V0aGVyIHRoZW4gY29yciA9CjAuIExldOKAmXMgbG9vayBhdCB3aGljaCB0ZXJtcyBjby1vY2N1ciB3aXRoIHRoZSB0ZXJtIOKAnG1lYXTigJ0sIHdpdGggYSBsb3dlciBjb3JyZWxhdGlvbiBsaW1pdCBvZiAwLjUuCmBgYHtyfQpmaW5kQXNzb2NzKGR0bVNwYXJzZVJlbW92ZWQsICJnb29kIiwgY29ybGltaXQ9MC41KQpgYGAKCk5leHQsIHdlIGNhbiBkbyBzb21lIHRleHQgdmlzdWFsaXNhdGlvbi4gRmlyc3QsIHdlIGNhbiBwbG90IG91ciBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGluIHZhcmlvdXMgd2F5cy4gRm9yCmV4YW1wbGUsIHVzaW5nIGEgYmFyY2hhcnQgdG8gdmlzdWFsaXNlIHRoZSAyMCBtb3N0IGZyZXF1ZW50IHRlcm1zICh3ZSB3aWxsIHVzZSB0aGUgbGF0dGljZSBwYWNrYWdlIGZvciBhCm5pY2UgYmFyIGNoYXJ0OgpgYGB7cn0KcmVxdWlyZShsYXR0aWNlKQojIHBuZygiYmFyY2hhcnRfZnJlcXVlbnRfdGVybXMucG5nIiwgd2lkdGg9ODAwLCBoZWlnaHQ9NzAwKQpiYXJjaGFydChmcmVxVGVybXNbb3JkZXJUZXJtc1sxOjIwXV0pCiMgZGV2Lm9mZigpCmBgYAoKCgojIyBXb3JkIENsb3VkCgpOZXh0LCB3ZSB3aWxsIGNvbnN0cnVjdCBhIGNvbXBhcmlzb24gd29yZCBjbG91ZCBvZiB0aGUgU3RhciBXYXJzIGFuZCBTdGFyIFRyZWsgZmFuIHBhZ2UgY29tbWVudHMuCmBgYHtyfQojIGNyZWF0ZSBhIGNoYXJhY3RlciB2ZWN0b3Igb2YgdGhlIFN0YXIgV2FycyBjb21tZW50cwojIChpLmUuLCB0YWtlIGEgc3Vic2V0IG9mIGVsZW1lbnRzIGZyb20gdGhlIGNvbW1lbnRUZXh0IGNvbHVtbiBvZiB0aGUgZGF0YWZyYW1lKQpzdGFyV2Fyc0NvbW1lbnRzIDwtIG15U3RhcldhcnNEYXRhJGNvbW1lbnRUZXh0W3doaWNoKG15U3RhcldhcnNEYXRhJGNvbW1lbnRUZXh0IT0iTm90X2FwcGxpY2FibGUiKV0Kc3RhcldhcnNDb21tZW50cyA8LSBwYXN0ZShzdGFyV2Fyc0NvbW1lbnRzICwgY29sbGFwc2UgPSAiICIpCgojIGRvIHRoZSBzYW1lLCBidXQgZm9yIFN0YXIgVHJlawpzdGFyVHJla0NvbW1lbnRzIDwtIG15U3RhclRyZWtEYXRhJGNvbW1lbnRUZXh0W3doaWNoKG15U3RhclRyZWtEYXRhJGNvbW1lbnRUZXh0IT0iTm90X2FwcGxpY2FibGUiKV0Kc3RhclRyZWtDb21tZW50cyA8LSBwYXN0ZShzdGFyVHJla0NvbW1lbnRzICwgY29sbGFwc2UgPSAiICIpCgojIGNvbWJpbmUgdGhlbSB0b2dldGhlciBpbnRvIGEgZGF0YWZyYW1lCmRmX0FMTCA8LSBkYXRhLmZyYW1lKGdyb3VwPWMoIlN0YXJfV2FycyIsIlN0YXJfVHJlayIpLHdvcmRzPWMoc3RhcldhcnNDb21tZW50cyxzdGFyVHJla0NvbW1lbnRzKSkKYGBgCgpEYXRhIHByZS1wcm9jZXNzaW5nOgpgYGB7cn0KIyBzZWFyY2ggZm9yIGFueSB0ZXh0cyB0aGF0IGhhdmUgbm8gY2hhcmFjdGVycyAoaS5lLiBhcmUg4oCZZW1wdHnigJkpCiMgYW5kIHRoZW4gcmVtb3ZlIHRoZXNlIGVsZW1lbnRzIGZyb20gdGhlIHZlY3Rvcgp0b1JlbW92ZSA8LSB3aGljaChkZl9BTEwkd29yZHM9PSIiKQoKIyBhcmUgdGhlcmUgYW55IOKAmWVtcHR54oCZIHRleHQgZWxlbWVudHM/CiMgKGkuZS4gbGVuZ3RoIG9mIHRvUmVtb3ZlIGlzIG5vdCBlcXVhbCB0byB6ZXJvKQojIGlmIHRydWUsIHRoZW4gd2UgcmVtb3ZlIHRoZSBjb3JyZXNwb25kaW5nIHJvd3MgZnJvbSB0aGUgZGF0YWZyYW1lCmlmIChpc1RSVUUobGVuZ3RoKHRvUmVtb3ZlKSE9MCkpIHsKZGZfQUxMIDwtIGRmX0FMTFstdG9SZW1vdmUsXQp9CiMgd2UgY3JlYXRlIGEgY2hhcmFjdGVyIHZlY3RvciBmcm9tIHRoZSAid29yZHMiIGNvbHVtbiBvZiBkZl9BTEwKIyB0aGlzIHdpbGwgYmUgb3VyIGluZGVwZW5kZW50IHZhcmlhYmxlLgojIHdlIGRvIG5vdCB3YW50IHRleHQgYXMgZmFjdG9ycywgc28gd2Ugd2lsbCBjb2VyY2UgaXQgdG8gY2hhcmFjdGVyCndvcmRzIDwtIGRmX0FMTCR3b3JkcwojIHdlIHdpbGwgY29udmVydCB0aGUgY2hhcmFjdGVyIGVuY29kaW5nIHRvIFVURi04CiMganVzdCB0byBiZSBzdXJlIHRoZXJlIGFyZSBubyBvZGQgY2hhcmFjdGVycyB0aGF0CiMgbWF5IGNhdXNlIHByb2JsZW1zIGxhdGVyIG9uCndvcmRzIDwtIGljb252KHdvcmRzLCB0byA9ICJVVEYtOCIpCiMgKiogTUFDIFVTRVJTIE9OTFkgKio6CndvcmRzIDwtIGljb252KHdvcmRzLCB0byA9ICJVVEYtOC1tYWMiKQojIHVzaW5nIOKAmXRt4oCZIHBhY2thZ2Ugd2UgY29udmVydCBjaGFyYWN0ZXIgdmVjdG9yIHRvIGEgVmNvcnB1cyBvYmplY3QgKHZvbGF0aWxlIGNvcnB1cykKY29ycCA8LSBWQ29ycHVzKFZlY3RvclNvdXJjZSh3b3JkcykpCiMjIG5vdyB3ZSBkbyB0cmFuc2Zvcm1hdGlvbnMgb2YgdGV4dCB1c2luZyB0bV9tYXAgKOKAmW1hcHBpbmcgdG8gdGhlIGNvcnB1c+KAmSkKIyBlbGltaW5hdGUgZXh0cmEgd2hpdGVzcGFjZQpjb3JwIDwtIHRtX21hcChjb3JwLCBzdHJpcFdoaXRlc3BhY2UpCiMgY29udmVydCB0byBhbGwgbG93ZXJjYXNlCmNvcnAgPC0gdG1fbWFwKGNvcnAsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCiMgcGVyZm9ybSBzdGVtbWluZyAobm90IGFsd2F5cyB1c2VmdWwhKQojZmJDb3JwdXMgPC0gdG1fbWFwKGZiQ29ycHVzLCBzdGVtRG9jdW1lbnQpCiMgcmVtb3ZlIG51bWJlcnMgKG5vdCBhbHdheXMgdXNlZnVsISkKZmJDb3JwdXMgPC0gdG1fbWFwKGZiQ29ycHVzLCByZW1vdmVOdW1iZXJzKQojIHJlbW92ZSBwdW5jdHVhdGlvbiAobm90IGFsd2F5cyB1c2VmdWwhIGUuZy4gdGV4dCBlbW90aWNvbnMpCmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pCiMgcmVtb3ZlIHN0b3Agd29yZHMgKG5vdCBhbHdheXMgdXNlZnVsISkgLSBkb2luZyB0aGlzIGluIHBlcmwKY29ycCA8LSB0bV9tYXAoY29ycCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKQojIGNyZWF0ZSBhIGRvY3VtZW50LXRlcm0gbWF0cml4CiMgaGFkIHRvIGRvIGl0IHRoaXMgd2F5IHRvIGJlIGFibGUgdG8gdXNlIGNvbG5hbWVzCnRkbSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycCkKdGRtIDwtIGFzLm1hdHJpeCh0ZG0pCiNwcmludCh0ZG0pCmNvbG5hbWVzKHRkbSkgPC0gYygiU3Rhcl9XYXJzIiwiU3Rhcl9UcmVrIikKY29sb3JzeD1jKCJibHVlIiwicmVkIikKYGBgCgpXb3JkIENsb3VkIHZpc3VhbGl6YXRpb246CmBgYHtyfQpyZXF1aXJlKHdvcmRjbG91ZCkKI25vdGU6IGlmIGNoYW5naW5nIHJlcyBvZiBwbmcsIGNhbuKAmXQgaGF2ZSBkaW1lbnNpb25zIGluIHBpeGVscyAobGVkIHRvIHdvcmRjbG91ZHMgd2l0aCB2ZXJ5IGZldyB3b3Jkcy4uLikKIyBwbmcoImZhY2Vib29rX3N0YXJ3YXJzX3N0YXJ0cmVrX2NvbXBhcmlzb25fY2xvdWQucG5nIiwgd2lkdGg9MTIsIGhlaWdodD04LCB1bml0cz0iaW4iLCByZXM9MzAwKQoKI2NvbXBhcmlzb24uY2xvdWQodGRtLG1heC53b3Jkcz0zMDAscmFuZG9tLm9yZGVyPUZBTFNFKQpjb21wYXJpc29uLmNsb3VkKHRkbSxtYXgud29yZHM9MjAwLHJhbmRvbS5vcmRlcj1GQUxTRSxjb2xvcnM9Y29sb3JzeCkKI2NvbW1vbmFsaXR5LmNsb3VkKHRkbSxyYW5kb20ub3JkZXI9RkFMU0UpCgojIGRldi5vZmYoKQpgYGAKCgoKCiMgU29jaWFsIE5ldHdvcmsgVmlzdWFsaXphdGlvbgoKIyMgU21hbGwtc2NhbGUgdmlzdWFsaXphdGlvbgoKIyMjIFlvdXR1YmUgYWN0b3IgbmV0d29yayAodXNlci11c2VyKQpXZSBjYW4gdmlzdWFsaXNlIGEgbmV0d29yayBieSBwbG90dGluZyBpdCBkaXJlY3RseSBpbiBSOgpgYGB7cn0KIyBwbmcoImdfYWN0b3JfeW91dHViZS5wbmciLCB3aWR0aD04MDAsIGhlaWdodD03MDApCnBsb3QoZ19hY3Rvcl95b3V0dWJlLCB2ZXJ0ZXguc2hhcGU9Im5vbmUiLCBlZGdlLndpZHRoPTEuNSwgZWRnZS5jdXJ2ZWQ9LjUsIGVkZ2UuYXJyb3cuc2l6ZT0wLjUsIGFzcD05LzE2LCBtYWluPSJVc2VycyBhcyBhY3RvciBuZXR3b3JrIikKIyBkZXYub2ZmKCkKYGBgCgojIyMgRmFjZWJvb2sgYmltb2RhbCBuZXR3b3JrICh1c2VyLXBvc3QpCgpCZWZvcmUgcGxvdHRpbmcgdGhlIGdyYXBoLCBjaGFuZ2UgdGhlIG5vZGUgY29sb3Igc3VjaCB0aGF0IFBvc3RzIGFyZSByZWQsIHdoaWxlIFVzZXJzIGFyZSB0aGUgZGVmYXVsdCBjb2xvcgooYmx1ZSkuCmBgYHtyfQpWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWspJGNvbG9yIDwtIGlmZWxzZShWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWspJHR5cGUgPT0gIlBvc3QiLCAicmVkIiwgImJsdWUiKQpgYGAKCldlIGNhbiBzZWUgdGhlIG5ldHdvcmsgd2l0aCB0aGUgZm9sbG93aW5nOgpgYGB7cn0KcGxvdChnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrKQpgYGAKCkluIFJTdHVkaW8sIHRoZSBwbG90IHBhbmUgaXMgZ2VuZXJhbGx5IHRvbyBzbWFsbCBhbmQgc28gYW4gaW1wcm92ZW1lbnQgaXMgdmlhIG9wZW5pbmcgYW4gWDExIGdyYXBoaWNzCmRyaXZlciAob25seSBvbiBtYWNoaW5lcyB3aXRoIGFjY2VzcyB0byBhbiBYIHNlcnZlcik6CmBgYHtyIGV2YWw9RkFMU0V9CngxMSgpCnBsb3QoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlaykKYGBgCgpUaGUgZm9sbG93aW5nIHNldCBvZiBjb21tYW5kcyBwcmludHMgdGhlIHBsb3QgKHdpdGggc29tZSBwbG90IG9wdGlvbnMgdG8gaW1wcm92ZSB0aGUgdmlzdWFsaXNhdGlvbikgdG8gZmlsZToKYGBge3IgZXZhbD1GQUxTRSwgdGlkeT1UUlVFfQojIHBuZygiZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlay5wbmciLCB3aWR0aD04MDAsIGhlaWdodD03MDApCnBsb3QoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlayx2ZXJ0ZXguc2hhcGU9Im5vbmUiLGVkZ2Uud2lkdGg9MS41LGVkZ2UuY3VydmVkID0gLjUsZWRnZS5hcnJvdy5zaXplPTAuNSx2ZXJ0ZXgubGFiZWwuY29sb3I9VihnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrKSRjb2xvcixhc3A9OS8xNixtYXJnaW49LTAuMTUpCiMgZGV2Lm9mZigpCmBgYAoKIyMjIFR3aXR0ZXIgYWN0b3IgbmV0d29yayAodXNlci11c2VyKQoKTmV4dCwgd2UgY2FuIHZpc3VhbGlzZSB0aGUgbmV0d29yayBieSBwbG90dGluZyBpdCBkaXJlY3RseSBpbiBSOgpgYGB7ciB0aWR5PVRSVUV9CiMgcG5nKCJnX3R3aXR0ZXJfYWN0b3IucG5nIiwgd2lkdGg9ODAwLCBoZWlnaHQ9NzAwKQpwbG90KGdfdHdpdHRlcl9hY3Rvcix2ZXJ0ZXguc2hhcGU9Im5vbmUiLGVkZ2Uud2lkdGg9MS41LGVkZ2UuY3VydmVkID0gLjUsZWRnZS5hcnJvdy5zaXplPTAuNSxhc3A9OS8xNixtYXJnaW49LTAuMTUpCiMgZGV2Lm9mZigpCmBgYAoKCgojIyBMYXJnZS1zY2FsZSB2aXN1YWxpemF0aW9uCgojIyMgSW5zdGFsbGluZyBHZWxwaGkKCllvdSBjYW4gZXhwb3J0IHRoZSBzb2NpYWwgbmV0d29yayBieSBSL1NvY2lhbE1lZGlhTGFiIGFuZCBpbXBvcnQgaXQgaW50byBHZXBoaSwKd2hpY2ggaXMgYSBmYW50YXN0aWMgbmV0d29yayB2aXN1YWxpc2F0aW9uIHByb2dyYW0uIElmIHlvdSB3aXNoIHRvIGRvIHRoaXMsIHRoZW4geW91IG5lZWQgdG8gZG93bmxvYWQgYW5kCmluc3RhbGwgW0dlcGhpXShodHRwczovL2dlcGhpLm9yZy91c2Vycy9kb3dubG9hZC8pLgoKVG8gZG8gbmV0d29yayBhbmFseXNpcyAoZS5nLiBleHBvcnQgbmV0d29yayB0byBmaWxlKSB3ZSBhbHNvIG5lZWQgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwgdGhlIGlncmFwaCBwYWNrYWdlIGNyZWF0ZWQgYnkgR2Fib3IgQ3NhcmRpLgpXZSBjYW4gZG8gdGhpcyBieSBlbnRlcmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW50byB0aGUgUiBjb25zb2xlOgoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiaWdyYXBoIikgIyBJbnN0YWxsIHBhY2thZ2UKYGBgCgpPciBsb2FkIHRoZSBwYWNrYWdlIGlmIGl0IGlzIGFscmVhZHkgaW5zdGFsbGVkOgpgYGB7cn0KcmVxdWlyZShpZ3JhcGgpICNMb2FkIHBhY2thZ2UKYGBgCgoKIyMjIFVzaW5nIEdlbHBoaQoKQmVjYXVzZSBvZiB0aGUgc2l6ZSBvZiB0aGUgdHdvIG5ldHdvcmtzIHdlIGhhdmUgY3JlYXRlZCwgd2Ugd29uJ3QgdHJ5IHRvIHZpc3VhbGlzZSB0aGVtIGluIFIuIFRvIHZpc3VhbGlzZSB0aGUgIlN0YXIgVHJlayIiIG5ldHdvcmtpbmcgdXNpbmcgR2VwaGksIGZpcnN0IGV4cG9ydCB0aGUgbmV0d29yayB1c2lnIHRoZSAnZ3JhcGhtbCcgbmV0d29yayBmaWxlIGZvcm1hdDoKYGBge3J9CndyaXRlLmdyYXBoKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWssICJnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrLmdyYXBobWwiLCBmb3JtYXQ9ImdyYXBobWwiKQpgYGAKCkdlcGhpIGlzIHRoZSBpZGVhbCBzb2Z0d2FyZSBmb3IgdmlzdWFsaXNpbmcgbmV0d29ya3MgY3JlYXRlZCBpbiBTb2NpYWxNZWRpYUxhYiAuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGltcG9ydAppbnRvIEdlcGhpIHRoZSBJbnN0YWdyYW0gbmV0d29yayB3ZSBnZW5lcmF0ZWQgZWFybGllciwgaW4gb3JkZXIgdG8gdmlzdWFsaXNlIGl0LgoKRmlyc3QsIG9wZW4gR2VwaGkuIFlvdSBzaG91bGQgYmUgcHJlc2VudGVkIHdpdGggYSBkaWFsb2d1ZSBib3guIENsaWNrIOKAnE9wZW4gR3JhcGggRmlsZS4uLuKAnSAob3Igc2VsZWN0IGZyb20KbWVudSDigJhGaWxl4oCZ4oCTPuKAmE9wZW7igJkpIGFuZCBuYXZpZ2F0ZSB0byB0aGUgd29ya2luZyBkaXJlY3Rvcnkgb2YgeW91ciBSIHByb2plY3QgZm9yIHRoaXMgdHV0b3JpYWwuIFRoZSB3b3JraW5nCmRpcmVjdG9yeSBzaG91bGQgY29udGFpbiBhIGdyYXBobWwgZmlsZSB0aGF0IHdhcyBnZW5lcmF0ZWQgaW4gdGhlIEluc3RhZ3JhbSBzZWN0aW9uLiBTZWxlY3QgdGhpcyBmaWxlIGFuZCBjbGljawpvcGVuLgoKWW91IHdpbGwgYmUgcHJlc2VudGVkIHdpdGggYW4g4oCYSW1wb3J0IHJlcG9ydOKAmSBkaWFsb2d1ZSBib3ggcHJvdmlkaW5nIGluZm9ybWF0aW9uIGFib3V0IHRoZSBuZXR3b3JrLiBTaW1wbHkKY2xpY2sgT0suIFlvdXIgbmV0d29yayB3aWxsIHRoZW4gYmUgcHJlc2VudGVkIGluIGEgdmVyeSBhd2Z1bCBmb3JtYXQgLSBwb3NzaWJseSBsb29raW5nIGxpa2UgYSBncmF5LWNvbG91cmVkCmhhaXJiYWxsLiBMZXTigJlzIGZpeCB0aGF0LgoKIVtdKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWsuZ2VscGhpLnBuZykKCkluIHRoZSB0b3AtbGVmdCBoYW5kIGJveCB1bmRlciB0aGUg4oCYQXBwZWFyYW5jZeKAmSB0YWIsIGNsaWNrIHRoZSBsaXR0bGUgaWNvbiB0aGF0IHNheXMg4oCYU2l6ZeKAmSB3aGVuIHlvdSBob3ZlciBvdmVyCml0LiBTZWxlY3Qg4oCcSW5kZWdyZWXigJ0gZnJvbSB0aGUgZHJvcGRvd24gbWVudS4gU2V0IHRoZSBtaW4gc2l6ZSB0byA1IGFuZCB0aGUgbWF4IHNpemUgdG8gNTAsIGFuZCBjbGljayBBcHBseS4KTm93IHRoZSBub2RlcyBpbiB5b3VyIG5ldHdvcmsgYXJlIHNpemVkIGJhc2VkIG9uIGhvdyBtYW55IGlubGlua3MgdGhleSByZWNlaXZlIGZyb20gb3RoZXIgbm9kZXMuCkl0J3Mgc3RpbGwgYW4gdWdseSBuZXR3b3JrIHRob3VnaCEKCiFbXShnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrLmdlbHBoaTIucG5nKQoK
Social Media Data Collection
Collecting Twitter data and creating social networks
In this section we will run through how to collect data from Twitter, create networks, and perform different kinds of analysis.
It is currently possible to create 3 different types of networks using Twitter data collected with SocialMediaLab. These are (1) actor networks; (2) bimodal networks; and (3) semantic networks.
First, define the API credentials. Due to the Twitter API specifications, it is not possible to save authentication tokens between sessions. The Authenticate() function is called only for its side effect, which provides access to the Twitter API for the current session.
Authenticating with the Twitter API
Go to Twitter Application Management and “Create New App”. Complete all fields in the form and create a new app. In your app page, go to “Keys and Access Tokens”, generate your access token, and copy the information to R:
Given that we are going to be creating two different types of Twitter networks (actor and semantic), we will Collect() the data, but not pipe it directly through to Network() straight away. This means we can reuse the data multiple times to create two different kinds of networks for analysis. We will collect 150 recent tweets that have used the #auspol hashtag. This is the dominant hashtag for Australian politics. The first step in the work flow is to authorise access the Twitter API. Instructions for obtaining Twitter API access are available from the VOSON website. See the previous section for a brief explanation of APIs.
We can have a quick look at the data we just collected:
Note the class of the dataframe, which lets SocialMediaLab know that this is an object of class dataSource , which we can then pass to the Create() function to generate different kinds of networks:
If you find that you are encountering errors possibly related to the text of the tweets, you can try converting the tweet text to UTF-8 character encoding. Roughly speaking, this command will help to deal with ‘odd’ characters in the text.
Mac users only may also wish to try the following if they are encountering errors that may be due to character encoding issues:
Creating social networks with Twitter data
Actor network
First, we will create an actor network. In this actor network, edges represent interactions between Twitter users. An interaction is defined as a “mention”" or “reply”" or “retweet” from user i to user j, given “tweet” m. In a nutshell, a Twitter actor network shows us who is interacting with who in relation to a particular hashtag or search term.
We can now examine the description of our network:
Semantic network
Next, we will create a semantic network. In this network nodes represent unique concepts (in this case unique terms/words extracted from a set of 150 tweets), and edges represent the co-occurrence of terms for all observations in the data set. For example, for this Twitter semantic network, nodes represent either hashtags (e.g. “#auspol”) or single terms (“politics”). If there are 150 tweets in the data set (i.e. 150 observations), and the term #auspol and the term politics appear together in every tweet, then this would be represented by an edge with weight equal to 150
Let’s have a look at the network description:
Bimodal network
Now that we have our Twitter data we can generate a bimodal network. This kind of network provides many possibilities for analysis and generating insights from our data.
In this bimodal network there are two types of nodes: users and hashtags. The bimodal network is therefore:
We now run the Create() function, which creates an igraph object called g_bimodal_twitter. Creating networks in SocialMediaLab is straightforward. We simply pass the myTwitterData object to the Create() function, and it takes care of the rest. We specify what kind of network we want to create (i.e. a bimodal network) by specifying this as an argument to the Create() function.
Note also that there is a tricky operator introduced here, the ‘pipe’ operator %>% , which we have not covered yet. This operator comes from the Magrittr package, and it is used to ‘pipe’ together commands in a chain, passing the values along the pipeline until it reaches the final command, which returns the output (i.e. the network we wish to create). In this instance we are passing (or “piping”) the data we collected using Collect() through to the Create() function.
We can now view basic information about the network:
Collecting Facebook data
In this section we will run through how to collect data from Facebook, create networks, and perform different kinds of analysis.
Authenticate with Facebook Developers API
The process of authentication, data collection, and creating social networks can be expressed with the 3 verb functions: Authenticate() , Collect() , and Create() . This simplified workflow exploits the pipe interface of the Magrittr package, and provides better handling of API authentication between R sessions. What we are doing is “piping” the data forward using the %>% operator, in a kind of functional programming approach. It means we can pipe together all the different elements of the work flow in a quick and easy manner. This also provides the ability to save and load authentication tokens, so we don’t have to keep authenticating with APIs between sessions. Obviously, this opens up possibilities for automation and data mining projects.
Go to Facebook Developers Page (https://developers.facebook.com/apps/), create a new app, and copy the appID and appSecret information.
Make sure we have our appID and appSecret values defined:
Save credential file for later access:
The first time you authenticate you will see this:
You have to paste url (http://localhost:1410/ ) in developer app settings: (i) Click on add platform. (ii) Choose website and paste http://localhost:1410/ in URL link.
Bimodal networks
First, we will collect 2 days worth of activity from the Star Wars official page. This will collect all the posts posted between the rangeFrom and rangeTo dates, including all comments and likes, and other associated data including usernames, timestamps for comments, etc. Note: the date format is YYYY-MM-DD.
We will be using this data to create a bimodal network. This graph object is bimodal because edges represent relationships between nodes of two different types. For example, in our bimodal Facebook network, nodes represent Facebook users or Facebook posts, and edges represent whether a user has commented or ‘liked’ a post. Edges are directed and weighted (e.g. if user i has commented n times on post j, then the weight of this directed edge equals n).
The Magrittr pipe approach used in this example means that we only end up with the final graph object (in the global environment). To ensure we retain the data that are collected, the argument writeToFile=TRUE is used. This writes the data collected using Collect() function to a local CSV file before it is piped through to the network generation function Create() . We can then read it in as a dataframe (see code snippet below).
This means we end up with two objects for further analysis, a graph object g_bimodal_facebook_star_wars , and a dataframe object myStarWarsData.
Before proceeding into analysis, we will collect 2 days worth of data from the Star Trek Facebook page, but this time we will pipe through the LoadCredential function, meaning that we are using the authentication token that we stored locally in the previous step.
Read in the data to a dataframe
Now we can perform some analysis on the Star Wars network. Firstly, we will run through some essential SNA techniques. After that we will do something a bit fancier, by comparing whether there are gender differences between Star Wars and Star Trek networks.
We can get descriptive information about the network:
This informs us that there are 1219 nodes and 1218 nodes in the network (this may differ somewhat for your own collected data). It tells us that our graph is Directed, Named, the edges are Weighted, and it also has the additional property of being a Bipartite graph.
Collecting YouTube video comment data
Authenticate
We first ensure that the SocialMediaLab package is loaded:
A Google Developer API Key is required for authenticating with the API (otherwise we cannot collect data). This requires a Google account. Instructions for obtaining a Google Developer API Key are available from the Youtube Data API Overview. Main steps:
We then run the following function, which ensures everything is correctly set up to access the API:
Get data
We now assign a character vector, specifying one or more YouTube video IDs that we wish to collect data from. For example, if the video URL is https://www.youtube.com/watch?v=W2GZFeYGU3s, then use videoIDs = ‘W2GZFeYGU3s’. Tip: for many videos, the function GetYoutubeVideoIDs can be used to create a vector object suitable as input for videoIDs.
We now collect the YouTube comment data and store it in a data frame object named myYoutubeData. This data frame can then be used for creating networks for further analysis. We can supply various arguments to the CollectDataYoutube function, providing various options for the data collection process.
We can examine the structure of our data frame:
Generate actor network
We will now create a unimodal network, a.k.a ‘actor network’, representing relationships between users who have interacted with each other. For YouTube comment threads, a relationship is defined as user i ‘replying to’ or ‘mentioning’ user j in a comment. In this network the vertices (a.k.a ‘nodes’) represent YouTube users and the edges (a.k.a ‘links’) represent whether (and how many times) user i has interacted with user j. The edges in this network are both directed and weighted. Edges are ‘directed’ because interactions may not be reciprocated (e.g. user i replies to user j, but user j does not reply to user i), and edges are also ‘weighted’ in order to show how many times user i has interacted with user j.
We can now view basic information about our network, notably the number of vertices (users) and number of interactions (edges):
Storing your social network in graphml format
You can export a social network generated by SocialMediaLab to “graphml” format using igraph package:
Now you can visualize the “graphml” file with Gelphi or other compatiable tools.