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:

myapikey <- "801xU41LxZmHrNh7NY3wVnB7C"
myapisecret <- "E4sATBjT3ZSARghmXgjrdcOZP5rVWHVXFxAwxvoG24QLN5oDQg"
myaccesstoken <- "19856730-XHDOuNW03D6Gqjv2JDQFxhZmurdsTe55nLMU48kjq" # avoids the browser authentication dance
myaccesstokensecret <- "2xFYrFO1yt1H0WzgHEjzqojf6ThNeBfC0f0bYJB2kwaQZ" # avoids the browser authentication dance

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.

require(SocialMediaLab)
require(magrittr)
myTwitterData <- Authenticate("twitter",
                              apiKey=myapikey,
                              apiSecret=myapisecret,
                              accessToken=myaccesstoken,
                              accessTokenSecret=myaccesstokensecret) %>%
Collect(searchTerm="#auspol", numTweets=150, writeToFile=FALSE, verbose=TRUE)
[1] "Using direct authentication"
Now retrieving data based on search term: #auspol
Done
Cleaning and sorting the data...
Done

We can have a quick look at the data we just collected:

View(myTwitterData)

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:

class(myTwitterData)
[1] "data.frame" "dataSource" "twitter"   

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.

myTwitterData$text <- iconv(myTwitterData$text, to = 'utf-8')

Mac users only may also wish to try the following if they are encountering errors that may be due to character encoding issues:

myTwitterData$text <- iconv(myTwitterData$text, to = 'utf-8-mac')

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.

g_twitter_actor <- myTwitterData %>% Create("Actor")
Generating the network...

Done.

We can now examine the description of our network:

g_twitter_actor
IGRAPH a7d7116 DN-- 152 223 -- 
+ attr: name (v/c), label (v/c), edgeType (e/c), timeStamp (e/c), tweet_id (e/c)
+ edges from a7d7116 (vertex names):
 [1] rainey_knight  ->JohnWren1950    Warstub        ->unionsaustralia ballgameskeith ->ClubeGaffer     atheist4maga   ->SydneyAtheist  
 [5] ochreblue      ->sammmw8         JoRobinson_Aus ->HuffPostAU      PreAmpPlus     ->GhostWhoVotes   croswell_g     ->SydneyAtheist  
 [9] ochreblue      ->lovethatloaf    plabg          ->GoldCoastNurse  ochreblue      ->hearyanow       ochreblue      ->tilleyfab      
[13] GoldSuzie      ->protecttheplain ochreblue      ->CorruptNSW      BobOfAlex      ->CFMEUJohnSetka  PreAmpPlus     ->unionsaustralia
[17] Firemonkey991  ->WendyFarmer_    PreAmpPlus     ->MGliksmanMDPhD  NaomiCra       ->LesStonehouse   TotalREProperty->FifiFoxfoot    
[21] dannytweeets   ->breko           plabg          ->protecttheplain ochreblue      ->stopcoalexports GoldSuzie      ->protecttheplain
[25] allisonBeDemure->protecttheplain KirstyWho      ->FrBower         Redbank80Graeme->protecttheplain stopcoalexports->Qldaah         
[29] InvestInScience->protecttheplain AliKayaBrisbane->Loud_Lass       PilligaPush    ->protecttheplain FeeFeeCee      ->MGliksmanMDPhD 
+ ... omitted several edges

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

g_twitter_semantic <- myTwitterData %>% Create("Semantic")
[1] "Generating Twitter semantic network..."

Done.

Let’s have a look at the network description:

g_twitter_semantic
IGRAPH 1de936d UNW- 54 121 -- 
+ attr: name (v/c), label (v/c), weight (e/n)
+ edges from 1de936d (vertex names):
 [1] #auspol        --auspol            #auspol        --turnbullmalcolm   auspol         --#nrlgf            turnbullmalcolm--#nrlgf           
 [5] #auspol        --ausunions         auspol         --#ausunions        ausunions      --#ausunions        auspol         --#qanda           
 [9] ausunions      --#qanda            #auspol        --adani             auspol         --#islam            auspol         --#paris           
[13] auspol         --#grandmufti       auspol         --#adani            adani          --#adani            #adani         --stopadani        
[17] auspol         --#stopadani        adani          --#stopadani        stopadani      --#stopadani        #auspol        --stopadani        
[21] auspol         --#marriageequality auspol         --#reachtel         auspol         --#manus            auspol         --#nauru           
[25] #stopadani     --corners           #auspol        --corners           auspol         --#4corners         adani          --#4corners        
[29] stopadani      --#4corners         corners        --#4corners         #auspol        --done              #auspol        --csg              
+ ... omitted several edges

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:

  • directed (users can use hashtags, but hashtags can’t use users)
  • weighted (users can comment multiple times on a post)
  • bipartite (users can like or comment on posts, but posts can’t like or comment back)
  • multiple edges or parallel edges (we have one edge for each interaction from user i to post j)

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.

g_bimodal_twitter <- myTwitterData %>% Create("Bimodal")
[1] "Generating Twitter bimodal network..."

Done

We can now view basic information about the network:

g_bimodal_twitter
IGRAPH 9009817 DN-- 100 160 -- 
+ attr: name (v/c), label (v/c), edgeType (e/c), timeStamp (e/c), tweet_id (e/c)
+ 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       
+ ... omitted several edges

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:

appID <- "2025204167698114" # Put your own api info here
appSecret <- "3a0b8f329e1ed9d803e02b5b3fdae831" # Put your own api info here

Save credential file for later access:

require(SocialMediaLab)
require(magrittr)
Authenticate("Facebook", appID = appID, appSecret = appSecret) %>% SaveCredential("FBCredential.RDS")

The first time you authenticate you will see this:

Copy and paste into Site URL on Facebook App Settings: http://localhost:1410/

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).

g_bimodal_facebook_star_wars <- 
  LoadCredential("FBCredential.RDS") %>% 
  Collect(pageName="StarWars", rangeFrom="2015-03-01", rangeTo="2015-03-02", writeToFile=TRUE) %>% 
  Create("Bimodal")
Now retrieving data from page: StarWars
2015-03-01  1 posts 
Now collecting data from POSTS (within the page).
 Collecting posts data from 1 posts.
Collecting maximum of 1000 comments and likes for each post.
.Facebook data was written to current working directory, with filename:
2015-03-01_to_2015-03-02_StarWars_FacebookData.csv

Creating Facebook bimodal network...

Done!

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).

# myStarWarsData <- read.csv("2015-03-01_to_2015-03-02_StarWars_FacebookData.csv")
myStarWarsData <- importData("2015-03-01_to_2015-03-02_StarWars_FacebookData.csv", "facebook")
View(myStarWarsData)
g_bimodal_facebook_star_wars <- myStarWarsData %>% Create("Bimodal")

Creating Facebook bimodal network...

Done!

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.

require(SocialMediaLab)
g_bimodal_facebook_star_trek <- 
  LoadCredential("FBCredential.RDS") %>%
  Collect(pageName="StarTrek", rangeFrom="2015-03-01", rangeTo="2015-03-02", writeToFile=TRUE) %>%
  Create("Bimodal")
Now retrieving data from page: StarTrek
2015-03-01  1 posts 
Now collecting data from POSTS (within the page).
 Collecting posts data from 1 posts.
Collecting maximum of 1000 comments and likes for each post.
.Facebook data was written to current working directory, with filename:
2015-03-01_to_2015-03-02_StarTrek_FacebookData.csv

Creating Facebook bimodal network...

Done!

Read in the data to a dataframe

#read.csv() is not equivalent to Collect()
myStarTrekData <- importData("2015-03-01_to_2015-03-02_StarTrek_FacebookData.csv", "facebook") 
#if you want to create network from local data:
# g_bimodal_facebook_star_trek <- myStarTrekData %>% Create("Bimodal")

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:

g_bimodal_facebook_star_wars
IGRAPH 8e21e39 DN-B 1208 1220 -- 
+ attr: name (v/c), type (v/c), label (v/c), edgeType (e/c), timestamp (e/c)
+ edges from 8e21e39 (vertex names):
 [1] AL Rbg              ->169299103121699_909270052457930 Abraham Gonzales    ->169299103121699_909270052457930
 [3] Abril Ramirez       ->169299103121699_909270052457930 Ace Falcon          ->169299103121699_909270052457930
 [5] Adam Blankenship    ->169299103121699_909270052457930 Adam Evenson        ->169299103121699_909270052457930
 [7] Adam Henshel        ->169299103121699_909270052457930 Adam Herb           ->169299103121699_909270052457930
 [9] Adam John Kiseloff  ->169299103121699_909270052457930 Adam Rollefson      ->169299103121699_909270052457930
[11] Adonis Lugo         ->169299103121699_909270052457930 Adrian Dobre        ->169299103121699_909270052457930
[13] Aidan Latham Daye   ->169299103121699_909270052457930 Aj Green            ->169299103121699_909270052457930
[15] Ajani Thomas        ->169299103121699_909270052457930 Akeem J. Webb       ->169299103121699_909270052457930
+ ... omitted several edges

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:

require(SocialMediaLab)
Loading required package: SocialMediaLab

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:

  1. You need a Google Account to access the Google Developers Console, request an API key, and register your application.
  2. Create a project in the Google Developers Console and obtain authorization credentials so your application can submit API requests.
  3. After creating your project, make sure the YouTube Data API is one of the services that your application is registered to use
my_apiKeyYoutube <- "AIzaSyAOS6z8kQjHJW5xL1kmjt8tYZd_JnzhpFE"

We then run the following function, which ensures everything is correctly set up to access the API:

apiKeyYoutube <- AuthenticateWithYoutubeAPI(my_apiKeyYoutube)

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.

videoIDs <- c("W2GZFeYGU3s","mL27TAJGlWc")

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.

myYoutubeData <- CollectDataYoutube(videoIDs, apiKeyYoutube, writeToFile=TRUE, verbose=TRUE, maxComments=100)

Now scraping video number: 1 (out of 2 videos in total).
Scraping a maximum of 100 comments for each video.
.................................................................................
Now scraping video number: 2 (out of 2 videos in total).
Scraping a maximum of 100 comments for each video.
.

We can examine the structure of our data frame:

str(myYoutubeData)
Classes 'dataSource', 'youtube' and 'data.frame':   135 obs. of  9 variables:
 $ Comment           : chr  "Pascal did not die: https://www.embarcadero.com/products/delphi\nFor a language guaranteed not to disappear wit"| __truncated__ "Is it safe to assume that user base of R are more business oriented people than hardcore CS or It professional?" "thank you" "Very good video" ...
 $ User              : chr  "Chuck Becker" "Swarnkar Rajesh" "Aparajito Sengupta" "James Piazza" ...
 $ ReplyCount        : chr  "0" "1" "0" "0" ...
 $ LikeCount         : chr  "0" "0" "0" "0" ...
 $ PublishTime       : chr  "2017-07-13T20:24:51.000Z" "2017-06-26T06:41:32.000Z" "2017-06-04T12:55:42.000Z" "2017-05-26T03:17:24.000Z" ...
 $ CommentId         : chr  "z13qvx3btvr3ehlps04cghkivy2wspoorao" "z12eu1qoamb5s3sjr04cdfnb3wa4vtobhqg0k" "z135jfthfzmexpivi232xxe5ltjqfpokk" "z13giraztku0h1out22xyr0qyyvvchz2t" ...
 $ ParentID          : chr  "None" "None" "None" "None" ...
 $ ReplyToAnotherUser: chr  "FALSE" "FALSE" "FALSE" "FALSE" ...
 $ VideoID           : chr  "W2GZFeYGU3s" "W2GZFeYGU3s" "W2GZFeYGU3s" "W2GZFeYGU3s" ...

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.

require(igraph)
g_actor_youtube <- myYoutubeData %>% Create("Actor")

Done!

We can now view basic information about our network, notably the number of vertices (users) and number of interactions (edges):

g_actor_youtube
IGRAPH 97a6d5d DNW- 36 41 -- 
+ attr: name (v/c), label (v/c), weight (e/n)
+ edges from 97a6d5d (vertex names):
 [1] SoHoTandCool                                                                                                             ->FarsightPress                 
 [2] FarsightPress                                                                                                            ->SoHoTandCool                  
 [3] Ed Boone                                                                                                                 ->PsycAndrew                    
 [4] bytejuggler                                                                                                              ->Tank8484                      
 [5] dagda825                                                                                                                 ->RenegadeThinking              
 [6] RenegadeThinking                                                                                                         ->dagda825                      
 [7] FarsightPress                                                                                                            ->Swarnkar Rajesh               
 [8] <U+0410><U+043B><U+0435><U+043A><U+0441><U+0430><U+043D><U+0434><U+0440> <U+0418><U+0432><U+0430><U+043D><U+043E><U+0432>->EvaSlash                      
+ ... omitted several edges

Storing your social network in graphml format

You can export a social network generated by SocialMediaLab to “graphml” format using igraph package:

require(igraph) # install.packages("igraph") if not installed yet

Now you can visualize the “graphml” file with Gelphi or other compatiable tools.

write.graph(g_bimodal_facebook_star_trek, "g_bimodal_facebook_star_trek.graphml", format="graphml")

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()

Social Network Visualization

Small-scale visualization

Youtube actor network (user-user)

We can visualise a network by plotting it directly in R:

# png("g_actor_youtube.png", width=800, height=700)
plot(g_actor_youtube, vertex.shape="none", edge.width=1.5, edge.curved=.5, edge.arrow.size=0.5, asp=9/16, main="Users as actor network")

# dev.off()

Facebook bimodal network (user-post)

Before plotting the graph, change the node color such that Posts are red, while Users are the default color (blue).

V(g_bimodal_facebook_star_trek)$color <- ifelse(V(g_bimodal_facebook_star_trek)$type == "Post", "red", "blue")

We can see the network with the following:

plot(g_bimodal_facebook_star_trek)

In RStudio, the plot pane is generally too small and so an improvement is via opening an X11 graphics driver (only on machines with access to an X server):

x11()
plot(g_bimodal_facebook_star_trek)

The following set of commands prints the plot (with some plot options to improve the visualisation) to file:

# png('g_bimodal_facebook_star_trek.png', width=800, height=700)
plot(g_bimodal_facebook_star_trek, vertex.shape = "none", edge.width = 1.5, 
    edge.curved = 0.5, edge.arrow.size = 0.5, vertex.label.color = V(g_bimodal_facebook_star_trek)$color, 
    asp = 9/16, margin = -0.15)
# dev.off()

Twitter actor network (user-user)

Next, we can visualise the network by plotting it directly in R:

# png('g_twitter_actor.png', width=800, height=700)
plot(g_twitter_actor, vertex.shape = "none", edge.width = 1.5, edge.curved = 0.5, 
    edge.arrow.size = 0.5, asp = 9/16, margin = -0.15)

# dev.off()

Large-scale visualization

Installing Gelphi

You can export the social network by R/SocialMediaLab and import it into Gephi, which is a fantastic network visualisation program. If you wish to do this, then you need to download and install Gephi.

To do network analysis (e.g. export network to file) we also need to download and install the igraph package created by Gabor Csardi. We can do this by entering the following command into the R console:

install.packages("igraph") # Install package

Or load the package if it is already installed:

require(igraph) #Load package

Using Gelphi

Because of the size of the two networks we have created, we won’t try to visualise them in R. To visualise the “Star Trek”" networking using Gephi, first export the network usig the ‘graphml’ network file format:

write.graph(g_bimodal_facebook_star_trek, "g_bimodal_facebook_star_trek.graphml", format="graphml")

Gephi is the ideal software for visualising networks created in SocialMediaLab . In this section we will import into Gephi the Instagram network we generated earlier, in order to visualise it.

First, open Gephi. You should be presented with a dialogue box. Click “Open Graph File…” (or select from menu ‘File’–>‘Open’) and navigate to the working directory of your R project for this tutorial. The working directory should contain a graphml file that was generated in the Instagram section. Select this file and click open.

You will be presented with an ‘Import report’ dialogue box providing information about the network. Simply click OK. Your network will then be presented in a very awful format - possibly looking like a gray-coloured hairball. Let’s fix that.

In the top-left hand box under the ‘Appearance’ tab, click the little icon that says ‘Size’ when you hover over it. Select “Indegree” from the dropdown menu. Set the min size to 5 and the max size to 50, and click Apply. Now the nodes in your network are sized based on how many inlinks they receive from other nodes. It’s still an ugly network though!

LS0tCnRpdGxlOiAiU29jaWFsTWVkaWFMYWIgLSBTb2NpYWwgTmV0d29yayBBbmFseXNpcyAoU05BKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogIHBkZl9kb2N1bWVudDoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleAogICAgdG9jOiB5ZXMKICB3b3JkX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgojIFNvY2lhbCBNZWRpYSBEYXRhIENvbGxlY3Rpb24KCiMjIENvbGxlY3RpbmcgVHdpdHRlciBkYXRhIGFuZCBjcmVhdGluZyBzb2NpYWwgbmV0d29ya3MKCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHJ1biB0aHJvdWdoIGhvdyB0byBjb2xsZWN0IGRhdGEgZnJvbSBUd2l0dGVyLCBjcmVhdGUgbmV0d29ya3MsIGFuZCBwZXJmb3JtIGRpZmZlcmVudApraW5kcyBvZiBhbmFseXNpcy4KCkl0IGlzIGN1cnJlbnRseSBwb3NzaWJsZSB0byBjcmVhdGUgMyBkaWZmZXJlbnQgdHlwZXMgb2YgbmV0d29ya3MgdXNpbmcgVHdpdHRlciBkYXRhIGNvbGxlY3RlZCB3aXRoIFNvY2lhbE1lZGlhTGFiLgpUaGVzZSBhcmUgKDEpIGFjdG9yIG5ldHdvcmtzOyAoMikgYmltb2RhbCBuZXR3b3JrczsgYW5kICgzKSBzZW1hbnRpYyBuZXR3b3Jrcy4gCgpGaXJzdCwgZGVmaW5lIHRoZSBBUEkgY3JlZGVudGlhbHMuIER1ZSB0byB0aGUgVHdpdHRlciBBUEkgc3BlY2lmaWNhdGlvbnMsIGl0IGlzIG5vdCBwb3NzaWJsZSB0byBzYXZlIGF1dGhlbnRpY2F0aW9uCnRva2VucyBiZXR3ZWVuIHNlc3Npb25zLiBUaGUgQXV0aGVudGljYXRlKCkgZnVuY3Rpb24gaXMgY2FsbGVkIG9ubHkgZm9yIGl0cyBzaWRlIGVmZmVjdCwgd2hpY2ggcHJvdmlkZXMgYWNjZXNzCnRvIHRoZSBUd2l0dGVyIEFQSSBmb3IgdGhlIGN1cnJlbnQgc2Vzc2lvbi4KCiMjIyBBdXRoZW50aWNhdGluZyB3aXRoIHRoZSBUd2l0dGVyIEFQSQoKR28gdG8gVHdpdHRlciBbQXBwbGljYXRpb24gTWFuYWdlbWVudF0oaHR0cHM6Ly9hcHBzLnR3aXR0ZXIuY29tLykgYW5kICJDcmVhdGUgTmV3IEFwcCIuIENvbXBsZXRlIGFsbCBmaWVsZHMgaW4gdGhlIGZvcm0gYW5kIGNyZWF0ZSBhIG5ldyBhcHAuIEluIHlvdXIgYXBwIHBhZ2UsIGdvIHRvICJLZXlzIGFuZCBBY2Nlc3MgVG9rZW5zIiwgZ2VuZXJhdGUgeW91ciBhY2Nlc3MgdG9rZW4sIGFuZCBjb3B5IHRoZSBpbmZvcm1hdGlvbiB0byBSOgpgYGB7cn0KbXlhcGlrZXkgPC0gIjgwMXhVNDFMeFptSHJOaDdOWTN3Vm5CN0MiICMgUHV0IHlvdXIgb3duIGFwaSBpbmZvIGhlcmUKbXlhcGlzZWNyZXQgPC0gIkU0c0FUQmpUM1pTQVJnaG1YZ2pyZGNPWlA1clZXSFZYRnhBd3h2b0cyNFFMTjVvRFFnIiAjIFB1dCB5b3VyIG93biBhcGkgaW5mbyBoZXJlCm15YWNjZXNzdG9rZW4gPC0gIjE5ODU2NzMwLVhIRE91TlcwM0Q2R3FqdjJKRFFGeGhabXVyZHNUZTU1bkxNVTQ4a2pxIiAjIFB1dCB5b3VyIG93biBhcGkgaW5mbyBoZXJlCm15YWNjZXNzdG9rZW5zZWNyZXQgPC0gIjJ4RllyRk8xeXQxSDBXemdIRWp6cW9qZjZUaE5lQmZDMGYwYllKQjJrd2FRWiIgIyBQdXQgeW91ciBvd24gYXBpIGluZm8gaGVyZQpgYGAKCkdpdmVuIHRoYXQgd2UgYXJlIGdvaW5nIHRvIGJlIGNyZWF0aW5nIHR3byBkaWZmZXJlbnQgdHlwZXMgb2YgVHdpdHRlciBuZXR3b3JrcyAoYWN0b3IgYW5kIHNlbWFudGljKSwgd2Ugd2lsbApDb2xsZWN0KCkgdGhlIGRhdGEsIGJ1dCBub3QgcGlwZSBpdCBkaXJlY3RseSB0aHJvdWdoIHRvIE5ldHdvcmsoKSBzdHJhaWdodCBhd2F5LiBUaGlzIG1lYW5zIHdlIGNhbiByZXVzZQp0aGUgZGF0YSBtdWx0aXBsZSB0aW1lcyB0byBjcmVhdGUgdHdvIGRpZmZlcmVudCBraW5kcyBvZiBuZXR3b3JrcyBmb3IgYW5hbHlzaXMuCldlIHdpbGwgY29sbGVjdCAxNTAgcmVjZW50IHR3ZWV0cyB0aGF0IGhhdmUgdXNlZCB0aGUgI2F1c3BvbCBoYXNodGFnLiBUaGlzIGlzIHRoZSBkb21pbmFudCBoYXNodGFnIGZvcgpBdXN0cmFsaWFuIHBvbGl0aWNzLgpUaGUgZmlyc3Qgc3RlcCBpbiB0aGUgd29yayBmbG93IGlzIHRvIGF1dGhvcmlzZSBhY2Nlc3MgdGhlIFR3aXR0ZXIgQVBJLiBJbnN0cnVjdGlvbnMgZm9yIG9idGFpbmluZyBUd2l0dGVyIEFQSQphY2Nlc3MgYXJlIGF2YWlsYWJsZSBmcm9tIHRoZSBWT1NPTiB3ZWJzaXRlLiBTZWUgdGhlIHByZXZpb3VzIHNlY3Rpb24gZm9yIGEgYnJpZWYgZXhwbGFuYXRpb24gb2YgQVBJcy4KYGBge3J9CnJlcXVpcmUoU29jaWFsTWVkaWFMYWIpCnJlcXVpcmUobWFncml0dHIpCm15VHdpdHRlckRhdGEgPC0gQXV0aGVudGljYXRlKCJ0d2l0dGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBpS2V5PW15YXBpa2V5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcGlTZWNyZXQ9bXlhcGlzZWNyZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY2Vzc1Rva2VuPW15YWNjZXNzdG9rZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY2Vzc1Rva2VuU2VjcmV0PW15YWNjZXNzdG9rZW5zZWNyZXQpICU+JQpDb2xsZWN0KHNlYXJjaFRlcm09IiNhdXNwb2wiLCBudW1Ud2VldHM9MTUwLCB3cml0ZVRvRmlsZT1GQUxTRSwgdmVyYm9zZT1UUlVFKQpgYGAKCldlIGNhbiBoYXZlIGEgcXVpY2sgbG9vayBhdCB0aGUgZGF0YSB3ZSBqdXN0IGNvbGxlY3RlZDoKYGBge3J9ClZpZXcobXlUd2l0dGVyRGF0YSkKYGBgCgpOb3RlIHRoZSBjbGFzcyBvZiB0aGUgZGF0YWZyYW1lLCB3aGljaCBsZXRzIFNvY2lhbE1lZGlhTGFiIGtub3cgdGhhdCB0aGlzIGlzIGFuIG9iamVjdCBvZiBjbGFzcyBkYXRhU291cmNlICwKd2hpY2ggd2UgY2FuIHRoZW4gcGFzcyB0byB0aGUgQ3JlYXRlKCkgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgZGlmZmVyZW50IGtpbmRzIG9mIG5ldHdvcmtzOgoKYGBge3J9CmNsYXNzKG15VHdpdHRlckRhdGEpCmBgYAoKSWYgeW91IGZpbmQgdGhhdCB5b3UgYXJlIGVuY291bnRlcmluZyBlcnJvcnMgcG9zc2libHkgcmVsYXRlZCB0byB0aGUgdGV4dCBvZiB0aGUgdHdlZXRzLCB5b3UgY2FuIHRyeSBjb252ZXJ0aW5nCnRoZSB0d2VldCB0ZXh0IHRvIFVURi04IGNoYXJhY3RlciBlbmNvZGluZy4gUm91Z2hseSBzcGVha2luZywgdGhpcyBjb21tYW5kIHdpbGwgaGVscCB0byBkZWFsIHdpdGggJ29kZCcKY2hhcmFjdGVycyBpbiB0aGUgdGV4dC4KYGBge3J9Cm15VHdpdHRlckRhdGEkdGV4dCA8LSBpY29udihteVR3aXR0ZXJEYXRhJHRleHQsIHRvID0gJ3V0Zi04JykKYGBgCgpNYWMgdXNlcnMgb25seSBtYXkgYWxzbyB3aXNoIHRvIHRyeSB0aGUgZm9sbG93aW5nIGlmIHRoZXkgYXJlIGVuY291bnRlcmluZyBlcnJvcnMgdGhhdCBtYXkgYmUgZHVlIHRvCmNoYXJhY3RlciBlbmNvZGluZyBpc3N1ZXM6CmBgYHtyfQpteVR3aXR0ZXJEYXRhJHRleHQgPC0gaWNvbnYobXlUd2l0dGVyRGF0YSR0ZXh0LCB0byA9ICd1dGYtOC1tYWMnKQpgYGAKCiMjIyBDcmVhdGluZyBzb2NpYWwgbmV0d29ya3Mgd2l0aCBUd2l0dGVyIGRhdGEKCiMjIyMgQWN0b3IgbmV0d29yawpGaXJzdCwgd2Ugd2lsbCBjcmVhdGUgYW4gYWN0b3IgbmV0d29yay4gSW4gdGhpcyBhY3RvciBuZXR3b3JrLCBlZGdlcyByZXByZXNlbnQgaW50ZXJhY3Rpb25zIGJldHdlZW4gVHdpdHRlcgp1c2Vycy4gQW4gaW50ZXJhY3Rpb24gaXMgZGVmaW5lZCBhcyBhICJtZW50aW9uIiIgb3IgInJlcGx5IiIgb3IgInJldHdlZXQiIGZyb20gdXNlciBpIHRvIHVzZXIgaiwgZ2l2ZW4gInR3ZWV0IiBtLiBJbgphIG51dHNoZWxsLCBhIFR3aXR0ZXIgYWN0b3IgbmV0d29yayBzaG93cyB1cyB3aG8gaXMgaW50ZXJhY3Rpbmcgd2l0aCB3aG8gaW4gcmVsYXRpb24gdG8gYSBwYXJ0aWN1bGFyIGhhc2h0YWcKb3Igc2VhcmNoIHRlcm0uCmBgYHtyfQpnX3R3aXR0ZXJfYWN0b3IgPC0gbXlUd2l0dGVyRGF0YSAlPiUgQ3JlYXRlKCJBY3RvciIpCmBgYAoKV2UgY2FuIG5vdyBleGFtaW5lIHRoZSBkZXNjcmlwdGlvbiBvZiBvdXIgbmV0d29yazoKYGBge3J9CmdfdHdpdHRlcl9hY3RvcgpgYGAKCiMjIyMgU2VtYW50aWMgbmV0d29yawoKWy8vXTogIyAoCm15VHdpdHRlckRhdGEgPC0gcmVhZC5jc3YoIk9jdF8wMl8xNV8wOF8zOF8yMDE3X0NFU1RfI2F1c3BvbF9Ud2l0dGVyRGF0YS5jc3YiKQopCgpOZXh0LCB3ZSB3aWxsIGNyZWF0ZSBhIHNlbWFudGljIG5ldHdvcmsuIEluIHRoaXMgbmV0d29yayBub2RlcyByZXByZXNlbnQgdW5pcXVlIGNvbmNlcHRzIChpbiB0aGlzIGNhc2UKdW5pcXVlIHRlcm1zL3dvcmRzIGV4dHJhY3RlZCBmcm9tIGEgc2V0IG9mIDE1MCB0d2VldHMpLCBhbmQgZWRnZXMgcmVwcmVzZW50IHRoZSBjby1vY2N1cnJlbmNlIG9mIHRlcm1zIGZvciBhbGwKb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhIHNldC4gRm9yIGV4YW1wbGUsIGZvciB0aGlzIFR3aXR0ZXIgc2VtYW50aWMgbmV0d29yaywgbm9kZXMgcmVwcmVzZW50IGVpdGhlciBoYXNodGFncwooZS5nLiAiI2F1c3BvbCIpIG9yIHNpbmdsZSB0ZXJtcyAoInBvbGl0aWNzIikuIElmIHRoZXJlIGFyZSAxNTAgdHdlZXRzIGluIHRoZSBkYXRhIHNldCAoaS5lLiAxNTAgb2JzZXJ2YXRpb25zKSwKYW5kIHRoZSB0ZXJtICNhdXNwb2wgYW5kIHRoZSB0ZXJtIHBvbGl0aWNzIGFwcGVhciB0b2dldGhlciBpbiBldmVyeSB0d2VldCwgdGhlbiB0aGlzIHdvdWxkIGJlIHJlcHJlc2VudGVkCmJ5IGFuIGVkZ2Ugd2l0aCB3ZWlnaHQgZXF1YWwgdG8gMTUwCmBgYHtyfQpnX3R3aXR0ZXJfc2VtYW50aWMgPC0gbXlUd2l0dGVyRGF0YSAlPiUgQ3JlYXRlKCJTZW1hbnRpYyIpCmBgYAoKTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIG5ldHdvcmsgZGVzY3JpcHRpb246CmBgYHtyfQpnX3R3aXR0ZXJfc2VtYW50aWMKYGBgCgojIyMjIEJpbW9kYWwgbmV0d29yawoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgVHdpdHRlciBkYXRhIHdlIGNhbiBnZW5lcmF0ZSBhIGJpbW9kYWwgbmV0d29yay4gVGhpcyBraW5kIG9mIG5ldHdvcmsgcHJvdmlkZXMKbWFueSBwb3NzaWJpbGl0aWVzIGZvciBhbmFseXNpcyBhbmQgZ2VuZXJhdGluZyBpbnNpZ2h0cyBmcm9tIG91ciBkYXRhLgoKSW4gdGhpcyBiaW1vZGFsIG5ldHdvcmsgdGhlcmUgYXJlIHR3byB0eXBlcyBvZiBub2RlczogdXNlcnMgYW5kIGhhc2h0YWdzLiBUaGUgYmltb2RhbCBuZXR3b3JrIGlzIHRoZXJlZm9yZToKCiogZGlyZWN0ZWQgKHVzZXJzIGNhbiB1c2UgaGFzaHRhZ3MsIGJ1dCBoYXNodGFncyBjYW4ndCB1c2UgdXNlcnMpCiogd2VpZ2h0ZWQgKHVzZXJzIGNhbiBjb21tZW50IG11bHRpcGxlIHRpbWVzIG9uIGEgcG9zdCkKKiBiaXBhcnRpdGUgKHVzZXJzIGNhbiBsaWtlIG9yIGNvbW1lbnQgb24gcG9zdHMsIGJ1dCBwb3N0cyBjYW4ndCBsaWtlIG9yIGNvbW1lbnQgYmFjaykKKiBtdWx0aXBsZSBlZGdlcyBvciBwYXJhbGxlbCBlZGdlcyAod2UgaGF2ZSBvbmUgZWRnZSBmb3IgZWFjaCBpbnRlcmFjdGlvbiBmcm9tIHVzZXIgaSB0byBwb3N0IGopCgpXZSBub3cgcnVuIHRoZSBDcmVhdGUoKSBmdW5jdGlvbiwgd2hpY2ggY3JlYXRlcyBhbiBpZ3JhcGggb2JqZWN0IGNhbGxlZCBnX2JpbW9kYWxfdHdpdHRlci4gQ3JlYXRpbmcKbmV0d29ya3MgaW4gU29jaWFsTWVkaWFMYWIgaXMgc3RyYWlnaHRmb3J3YXJkLiBXZSBzaW1wbHkgcGFzcyB0aGUgbXlUd2l0dGVyRGF0YSBvYmplY3QgdG8gdGhlIENyZWF0ZSgpCmZ1bmN0aW9uLCBhbmQgaXQgdGFrZXMgY2FyZSBvZiB0aGUgcmVzdC4gV2Ugc3BlY2lmeSB3aGF0IGtpbmQgb2YgbmV0d29yayB3ZSB3YW50IHRvIGNyZWF0ZSAoaS5lLiBhIGJpbW9kYWwKbmV0d29yaykgYnkgc3BlY2lmeWluZyB0aGlzIGFzIGFuIGFyZ3VtZW50IHRvIHRoZSBDcmVhdGUoKSBmdW5jdGlvbi4KCk5vdGUgYWxzbyB0aGF0IHRoZXJlIGlzIGEgdHJpY2t5IG9wZXJhdG9yIGludHJvZHVjZWQgaGVyZSwgdGhlICdwaXBlJyBvcGVyYXRvciAlPiUgLCB3aGljaCB3ZSBoYXZlIG5vdCBjb3ZlcmVkIHlldC4gVGhpcyBvcGVyYXRvciBjb21lcyBmcm9tIHRoZSBNYWdyaXR0ciBwYWNrYWdlLCBhbmQgaXQgaXMgdXNlZCB0byAncGlwZScgdG9nZXRoZXIgY29tbWFuZHMgaW4gYSBjaGFpbiwgcGFzc2luZyB0aGUgdmFsdWVzIGFsb25nIHRoZSBwaXBlbGluZSB1bnRpbCBpdCByZWFjaGVzIHRoZSBmaW5hbCBjb21tYW5kLCB3aGljaCByZXR1cm5zIHRoZSBvdXRwdXQgKGkuZS4gdGhlIG5ldHdvcmsgd2Ugd2lzaCB0byBjcmVhdGUpLiBJbiB0aGlzIGluc3RhbmNlIHdlIGFyZSBwYXNzaW5nIChvciAicGlwaW5nIikgdGhlIGRhdGEgd2UgY29sbGVjdGVkIHVzaW5nIENvbGxlY3QoKSB0aHJvdWdoIHRvIHRoZSBDcmVhdGUoKQpmdW5jdGlvbi4KYGBge3J9CmdfYmltb2RhbF90d2l0dGVyIDwtIG15VHdpdHRlckRhdGEgJT4lIENyZWF0ZSgiQmltb2RhbCIpCmBgYAoKV2UgY2FuIG5vdyB2aWV3IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSBuZXR3b3JrOgpgYGB7cn0KZ19iaW1vZGFsX3R3aXR0ZXIKYGBgCgojIyBDb2xsZWN0aW5nIEZhY2Vib29rIGRhdGEKCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHJ1biB0aHJvdWdoIGhvdyB0byBjb2xsZWN0IGRhdGEgZnJvbSBGYWNlYm9vaywgY3JlYXRlIG5ldHdvcmtzLCBhbmQgcGVyZm9ybSBkaWZmZXJlbnQKa2luZHMgb2YgYW5hbHlzaXMuCgojIyMgQXV0aGVudGljYXRlIHdpdGggRmFjZWJvb2sgRGV2ZWxvcGVycyBBUEkKClRoZSBwcm9jZXNzIG9mIGF1dGhlbnRpY2F0aW9uLCBkYXRhIGNvbGxlY3Rpb24sIGFuZCBjcmVhdGluZyBzb2NpYWwgbmV0d29ya3MgY2FuIGJlIGV4cHJlc3NlZCB3aXRoIHRoZSAzIHZlcmIKZnVuY3Rpb25zOiBBdXRoZW50aWNhdGUoKSAsIENvbGxlY3QoKSAsIGFuZCBDcmVhdGUoKSAuIFRoaXMgc2ltcGxpZmllZCB3b3JrZmxvdyBleHBsb2l0cyB0aGUgcGlwZSBpbnRlcmZhY2UKb2YgdGhlIE1hZ3JpdHRyIHBhY2thZ2UsIGFuZCBwcm92aWRlcyBiZXR0ZXIgaGFuZGxpbmcgb2YgQVBJIGF1dGhlbnRpY2F0aW9uIGJldHdlZW4gUiBzZXNzaW9ucy4gCldoYXQgd2UgYXJlIGRvaW5nIGlzICJwaXBpbmciIHRoZSBkYXRhIGZvcndhcmQgdXNpbmcgdGhlICU+JSBvcGVyYXRvciwgaW4gYSBraW5kIG9mIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcKYXBwcm9hY2guIEl0IG1lYW5zIHdlIGNhbiBwaXBlIHRvZ2V0aGVyIGFsbCB0aGUgZGlmZmVyZW50IGVsZW1lbnRzIG9mIHRoZSB3b3JrIGZsb3cgaW4gYSBxdWljayBhbmQgZWFzeQptYW5uZXIuIFRoaXMgYWxzbyBwcm92aWRlcyB0aGUgYWJpbGl0eSB0byBzYXZlIGFuZCBsb2FkIGF1dGhlbnRpY2F0aW9uIHRva2Vucywgc28gd2UgZG9u4oCZdCBoYXZlIHRvIGtlZXAgYXV0aGVudGljYXRpbmcKd2l0aCBBUElzIGJldHdlZW4gc2Vzc2lvbnMuIE9idmlvdXNseSwgdGhpcyBvcGVucyB1cCBwb3NzaWJpbGl0aWVzIGZvciBhdXRvbWF0aW9uIGFuZCBkYXRhIG1pbmluZyBwcm9qZWN0cy4KCkdvIHRvIEZhY2Vib29rIERldmVsb3BlcnMgUGFnZSAoaHR0cHM6Ly9kZXZlbG9wZXJzLmZhY2Vib29rLmNvbS9hcHBzLyksIGNyZWF0ZSBhIG5ldyBhcHAsIGFuZCBjb3B5IHRoZSBhcHBJRCBhbmQgYXBwU2VjcmV0IGluZm9ybWF0aW9uLgoKTWFrZSBzdXJlIHdlIGhhdmUgb3VyIGFwcElEIGFuZCBhcHBTZWNyZXQgdmFsdWVzIGRlZmluZWQ6CmBgYHtyfQphcHBJRCA8LSAiMjAyNTIwNDE2NzY5ODExNCIgIyBQdXQgeW91ciBvd24gYXBpIGluZm8gaGVyZQphcHBTZWNyZXQgPC0gIjNhMGI4ZjMyOWUxZWQ5ZDgwM2UwMmI1YjNmZGFlODMxIiAjIFB1dCB5b3VyIG93biBhcGkgaW5mbyBoZXJlCmBgYAoKU2F2ZSBjcmVkZW50aWFsIGZpbGUgZm9yIGxhdGVyIGFjY2VzczoKYGBge3IgZXZhbD1GQUxTRX0KcmVxdWlyZShTb2NpYWxNZWRpYUxhYikKcmVxdWlyZShtYWdyaXR0cikKQXV0aGVudGljYXRlKCJGYWNlYm9vayIsIGFwcElEID0gYXBwSUQsIGFwcFNlY3JldCA9IGFwcFNlY3JldCkgJT4lIFNhdmVDcmVkZW50aWFsKCJGQkNyZWRlbnRpYWwuUkRTIikKYGBgCgpUaGUgZmlyc3QgdGltZSB5b3UgYXV0aGVudGljYXRlIHlvdSB3aWxsIHNlZSB0aGlzOgoKPkNvcHkgYW5kIHBhc3RlIGludG8gU2l0ZSBVUkwgb24gRmFjZWJvb2sgQXBwIFNldHRpbmdzOiBodHRwOi8vbG9jYWxob3N0OjE0MTAvCgpZb3UgaGF2ZSB0byBwYXN0ZSB1cmwgKGh0dHA6Ly9sb2NhbGhvc3Q6MTQxMC8gKSBpbiBkZXZlbG9wZXIgYXBwIHNldHRpbmdzOiAoaSkgQ2xpY2sgb24gYWRkIHBsYXRmb3JtLiAoaWkpIENob29zZSB3ZWJzaXRlIGFuZCBwYXN0ZSBodHRwOi8vbG9jYWxob3N0OjE0MTAvIGluIFVSTCBsaW5rLgoKIyMjIEJpbW9kYWwgbmV0d29ya3MKCkZpcnN0LCB3ZSB3aWxsIGNvbGxlY3QgMiBkYXlzIHdvcnRoIG9mIGFjdGl2aXR5IGZyb20gdGhlIFN0YXIgV2FycyBvZmZpY2lhbCBwYWdlLiBUaGlzIHdpbGwgY29sbGVjdCBhbGwgdGhlIHBvc3RzCnBvc3RlZCBiZXR3ZWVuIHRoZSByYW5nZUZyb20gYW5kIHJhbmdlVG8gZGF0ZXMsIGluY2x1ZGluZyBhbGwgY29tbWVudHMgYW5kIGxpa2VzLCBhbmQgb3RoZXIgYXNzb2NpYXRlZApkYXRhIGluY2x1ZGluZyB1c2VybmFtZXMsIHRpbWVzdGFtcHMgZm9yIGNvbW1lbnRzLCBldGMuIE5vdGU6IHRoZSBkYXRlIGZvcm1hdCBpcyBZWVlZLU1NLURELgoKV2Ugd2lsbCBiZSB1c2luZyB0aGlzIGRhdGEgdG8gY3JlYXRlIGEgYmltb2RhbCBuZXR3b3JrLiBUaGlzIGdyYXBoIG9iamVjdCBpcyBiaW1vZGFsIGJlY2F1c2UgZWRnZXMgcmVwcmVzZW50CnJlbGF0aW9uc2hpcHMgYmV0d2VlbiBub2RlcyBvZiB0d28gZGlmZmVyZW50IHR5cGVzLiBGb3IgZXhhbXBsZSwgaW4gb3VyIGJpbW9kYWwgRmFjZWJvb2sgbmV0d29yaywgbm9kZXMKcmVwcmVzZW50IEZhY2Vib29rIHVzZXJzIG9yIEZhY2Vib29rIHBvc3RzLCBhbmQgZWRnZXMgcmVwcmVzZW50IHdoZXRoZXIgYSB1c2VyIGhhcyBjb21tZW50ZWQgb3Ig4oCYbGlrZWTigJkgYQpwb3N0LiBFZGdlcyBhcmUgZGlyZWN0ZWQgYW5kIHdlaWdodGVkIChlLmcuIGlmIHVzZXIgaSBoYXMgY29tbWVudGVkIG4gdGltZXMgb24gcG9zdCBqLCB0aGVuIHRoZSB3ZWlnaHQgb2YgdGhpcwpkaXJlY3RlZCBlZGdlIGVxdWFscyBuKS4KCmBgYHtyfQpnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzIDwtIAogIExvYWRDcmVkZW50aWFsKCJGQkNyZWRlbnRpYWwuUkRTIikgJT4lIAogIENvbGxlY3QocGFnZU5hbWU9IlN0YXJXYXJzIiwgcmFuZ2VGcm9tPSIyMDE1LTAzLTAxIiwgcmFuZ2VUbz0iMjAxNS0wMy0wMiIsIHdyaXRlVG9GaWxlPVRSVUUpICU+JSAKICBDcmVhdGUoIkJpbW9kYWwiKQpgYGAKClRoZSBNYWdyaXR0ciBwaXBlIGFwcHJvYWNoIHVzZWQgaW4gdGhpcyBleGFtcGxlIG1lYW5zIHRoYXQgd2Ugb25seSBlbmQgdXAgd2l0aCB0aGUgZmluYWwgZ3JhcGggb2JqZWN0IChpbgp0aGUgZ2xvYmFsIGVudmlyb25tZW50KS4gVG8gZW5zdXJlIHdlIHJldGFpbiB0aGUgZGF0YSB0aGF0IGFyZSBjb2xsZWN0ZWQsIHRoZSBhcmd1bWVudCB3cml0ZVRvRmlsZT1UUlVFIGlzCnVzZWQuIFRoaXMgd3JpdGVzIHRoZSBkYXRhIGNvbGxlY3RlZCB1c2luZyBDb2xsZWN0KCkgZnVuY3Rpb24gdG8gYSBsb2NhbCBDU1YgZmlsZSBiZWZvcmUgaXQgaXMgcGlwZWQgdGhyb3VnaCB0bwp0aGUgbmV0d29yayBnZW5lcmF0aW9uIGZ1bmN0aW9uIENyZWF0ZSgpIC4gV2UgY2FuIHRoZW4gcmVhZCBpdCBpbiBhcyBhIGRhdGFmcmFtZSAoc2VlIGNvZGUgc25pcHBldCBiZWxvdykuCmBgYHtyfQojIG15U3RhcldhcnNEYXRhIDwtIHJlYWQuY3N2KCIyMDE1LTAzLTAxX3RvXzIwMTUtMDMtMDJfU3RhcldhcnNfRmFjZWJvb2tEYXRhLmNzdiIpCm15U3RhcldhcnNEYXRhIDwtIGltcG9ydERhdGEoIjIwMTUtMDMtMDFfdG9fMjAxNS0wMy0wMl9TdGFyV2Fyc19GYWNlYm9va0RhdGEuY3N2IiwgImZhY2Vib29rIikKVmlldyhteVN0YXJXYXJzRGF0YSkKZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfd2FycyA8LSBteVN0YXJXYXJzRGF0YSAlPiUgQ3JlYXRlKCJCaW1vZGFsIikKYGBgCgpUaGlzIG1lYW5zIHdlIGVuZCB1cCB3aXRoIHR3byBvYmplY3RzIGZvciBmdXJ0aGVyIGFuYWx5c2lzLCBhIGdyYXBoIG9iamVjdCBnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzICwKYW5kIGEgZGF0YWZyYW1lIG9iamVjdCBteVN0YXJXYXJzRGF0YS4KCkJlZm9yZSBwcm9jZWVkaW5nIGludG8gYW5hbHlzaXMsIHdlIHdpbGwgY29sbGVjdCAyIGRheXMgd29ydGggb2YgZGF0YSBmcm9tIHRoZSBTdGFyIFRyZWsgRmFjZWJvb2sgcGFnZSwgYnV0CnRoaXMgdGltZSB3ZSB3aWxsIHBpcGUgdGhyb3VnaCB0aGUgTG9hZENyZWRlbnRpYWwgZnVuY3Rpb24sIG1lYW5pbmcgdGhhdCB3ZSBhcmUgdXNpbmcgdGhlIGF1dGhlbnRpY2F0aW9uCnRva2VuIHRoYXQgd2Ugc3RvcmVkIGxvY2FsbHkgaW4gdGhlIHByZXZpb3VzIHN0ZXAuCmBgYHtyfQpyZXF1aXJlKFNvY2lhbE1lZGlhTGFiKQpnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrIDwtIAogIExvYWRDcmVkZW50aWFsKCJGQkNyZWRlbnRpYWwuUkRTIikgJT4lCiAgQ29sbGVjdChwYWdlTmFtZT0iU3RhclRyZWsiLCByYW5nZUZyb209IjIwMTUtMDMtMDEiLCByYW5nZVRvPSIyMDE1LTAzLTAyIiwgd3JpdGVUb0ZpbGU9VFJVRSkgJT4lCiAgQ3JlYXRlKCJCaW1vZGFsIikKYGBgCgpSZWFkIGluIHRoZSBkYXRhIHRvIGEgZGF0YWZyYW1lCmBgYHtyfQojcmVhZC5jc3YoKSBpcyBub3QgZXF1aXZhbGVudCB0byBDb2xsZWN0KCkKbXlTdGFyVHJla0RhdGEgPC0gaW1wb3J0RGF0YSgiMjAxNS0wMy0wMV90b18yMDE1LTAzLTAyX1N0YXJUcmVrX0ZhY2Vib29rRGF0YS5jc3YiLCAiZmFjZWJvb2siKSAKCiNpZiB5b3Ugd2FudCB0byBjcmVhdGUgbmV0d29yayBmcm9tIGxvY2FsIGRhdGE6CiMgZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlayA8LSBteVN0YXJUcmVrRGF0YSAlPiUgQ3JlYXRlKCJCaW1vZGFsIikKYGBgCgpOb3cgd2UgY2FuIHBlcmZvcm0gc29tZSBhbmFseXNpcyBvbiB0aGUgU3RhciBXYXJzIG5ldHdvcmsuIEZpcnN0bHksIHdlIHdpbGwgcnVuIHRocm91Z2ggc29tZSBlc3NlbnRpYWwKU05BIHRlY2huaXF1ZXMuIEFmdGVyIHRoYXQgd2Ugd2lsbCBkbyBzb21ldGhpbmcgYSBiaXQgZmFuY2llciwgYnkgY29tcGFyaW5nIHdoZXRoZXIgdGhlcmUgYXJlIGdlbmRlcgpkaWZmZXJlbmNlcyBiZXR3ZWVuIFN0YXIgV2FycyBhbmQgU3RhciBUcmVrIG5ldHdvcmtzLgoKV2UgY2FuIGdldCBkZXNjcmlwdGl2ZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbmV0d29yazoKYGBge3J9CmdfYmltb2RhbF9mYWNlYm9va19zdGFyX3dhcnMKYGBgCgpUaGlzIGluZm9ybXMgdXMgdGhhdCB0aGVyZSBhcmUgMTIxOSBub2RlcyBhbmQgMTIxOCBub2RlcyBpbiB0aGUgbmV0d29yayAodGhpcyBtYXkgZGlmZmVyIHNvbWV3aGF0IGZvciB5b3VyCm93biBjb2xsZWN0ZWQgZGF0YSkuIEl0IHRlbGxzIHVzIHRoYXQgb3VyIGdyYXBoIGlzIERpcmVjdGVkLCBOYW1lZCwgdGhlIGVkZ2VzIGFyZSBXZWlnaHRlZCwgYW5kIGl0IGFsc28gaGFzCnRoZSBhZGRpdGlvbmFsIHByb3BlcnR5IG9mIGJlaW5nIGEgQmlwYXJ0aXRlIGdyYXBoLgoKIyMgQ29sbGVjdGluZyBZb3VUdWJlIHZpZGVvIGNvbW1lbnQgZGF0YQoKIyMjIEF1dGhlbnRpY2F0ZQoKV2UgZmlyc3QgZW5zdXJlIHRoYXQgdGhlIFNvY2lhbE1lZGlhTGFiIHBhY2thZ2UgaXMgbG9hZGVkOgpgYGB7cn0KcmVxdWlyZShTb2NpYWxNZWRpYUxhYikKYGBgCgoKQSBHb29nbGUgRGV2ZWxvcGVyIEFQSSBLZXkgaXMgcmVxdWlyZWQgZm9yIGF1dGhlbnRpY2F0aW5nIHdpdGggdGhlIEFQSSAob3RoZXJ3aXNlIHdlIGNhbm5vdCBjb2xsZWN0IGRhdGEpLiBUaGlzIHJlcXVpcmVzIGEgR29vZ2xlIGFjY291bnQuIEluc3RydWN0aW9ucyBmb3Igb2J0YWluaW5nIGEgR29vZ2xlIERldmVsb3BlciBBUEkgS2V5IGFyZSBhdmFpbGFibGUgZnJvbSB0aGUgW1lvdXR1YmUgRGF0YSBBUEkgT3ZlcnZpZXddKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3lvdXR1YmUvdjMvZ2V0dGluZy1zdGFydGVkKS4gTWFpbiBzdGVwczoKCjEuIFlvdSBuZWVkIGEgR29vZ2xlIEFjY291bnQgdG8gYWNjZXNzIHRoZSBHb29nbGUgRGV2ZWxvcGVycyBDb25zb2xlLCByZXF1ZXN0IGFuIEFQSSBrZXksIGFuZCByZWdpc3RlciB5b3VyIGFwcGxpY2F0aW9uLgoyLiBDcmVhdGUgYSBwcm9qZWN0IGluIHRoZSBHb29nbGUgRGV2ZWxvcGVycyBDb25zb2xlIGFuZCBvYnRhaW4gYXV0aG9yaXphdGlvbiBjcmVkZW50aWFscyBzbyB5b3VyIGFwcGxpY2F0aW9uIGNhbiBzdWJtaXQgQVBJIHJlcXVlc3RzLgozLiBBZnRlciBjcmVhdGluZyB5b3VyIHByb2plY3QsIG1ha2Ugc3VyZSB0aGUgWW91VHViZSBEYXRhIEFQSSBpcyBvbmUgb2YgdGhlIHNlcnZpY2VzIHRoYXQgeW91ciBhcHBsaWNhdGlvbiBpcyByZWdpc3RlcmVkIHRvIHVzZQoKYGBge3J9Cm15X2FwaUtleVlvdXR1YmUgPC0gIkFJemFTeUFPUzZ6OGtRakhKVzV4TDFrbWp0OHRZWmRfSm56aHBGRSIKYGBgCgpXZSB0aGVuIHJ1biB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uLCB3aGljaCBlbnN1cmVzIGV2ZXJ5dGhpbmcgaXMgY29ycmVjdGx5IHNldCB1cCB0byBhY2Nlc3MgdGhlIEFQSToKYGBge3J9CmFwaUtleVlvdXR1YmUgPC0gQXV0aGVudGljYXRlV2l0aFlvdXR1YmVBUEkobXlfYXBpS2V5WW91dHViZSkKYGBgCgojIyMgR2V0IGRhdGEKCldlIG5vdyBhc3NpZ24gYSBjaGFyYWN0ZXIgdmVjdG9yLCBzcGVjaWZ5aW5nIG9uZSBvciBtb3JlIFlvdVR1YmUgdmlkZW8gSURzIHRoYXQgd2Ugd2lzaCB0byBjb2xsZWN0IGRhdGEgZnJvbS4gRm9yIGV4YW1wbGUsIGlmIHRoZSB2aWRlbyBVUkwgaXMgaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1XMkdaRmVZR1UzcywgdGhlbiB1c2UgdmlkZW9JRHMgPSAnVzJHWkZlWUdVM3MnLiBUaXA6IGZvciBtYW55IHZpZGVvcywgdGhlIGZ1bmN0aW9uIEdldFlvdXR1YmVWaWRlb0lEcyBjYW4gYmUgdXNlZCB0byBjcmVhdGUgYSB2ZWN0b3Igb2JqZWN0IHN1aXRhYmxlIGFzIGlucHV0IGZvciB2aWRlb0lEcy4KYGBge3J9CnZpZGVvSURzIDwtIGMoIlcyR1pGZVlHVTNzIiwibUwyN1RBSkdsV2MiKQpgYGAKCldlIG5vdyBjb2xsZWN0IHRoZSBZb3VUdWJlIGNvbW1lbnQgZGF0YSBhbmQgc3RvcmUgaXQgaW4gYSBkYXRhIGZyYW1lIG9iamVjdCBuYW1lZCBteVlvdXR1YmVEYXRhLiBUaGlzIGRhdGEgZnJhbWUgY2FuIHRoZW4gYmUgdXNlZCBmb3IgY3JlYXRpbmcgbmV0d29ya3MgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIFdlIGNhbiBzdXBwbHkgdmFyaW91cyBhcmd1bWVudHMgdG8gdGhlIENvbGxlY3REYXRhWW91dHViZSBmdW5jdGlvbiwgcHJvdmlkaW5nIHZhcmlvdXMgb3B0aW9ucyBmb3IgdGhlIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzLgpgYGB7ciBjYWNoZT1UUlVFfQpteVlvdXR1YmVEYXRhIDwtIENvbGxlY3REYXRhWW91dHViZSh2aWRlb0lEcywgYXBpS2V5WW91dHViZSwgd3JpdGVUb0ZpbGU9VFJVRSwgdmVyYm9zZT1UUlVFLCBtYXhDb21tZW50cz0xMDApCmBgYAoKV2UgY2FuIGV4YW1pbmUgdGhlIHN0cnVjdHVyZSBvZiBvdXIgZGF0YSBmcmFtZToKYGBge3J9CnN0cihteVlvdXR1YmVEYXRhKQpgYGAKCiMjIyBHZW5lcmF0ZSBhY3RvciBuZXR3b3JrCgpXZSB3aWxsIG5vdyBjcmVhdGUgYSB1bmltb2RhbCBuZXR3b3JrLCBhLmsuYSAnYWN0b3IgbmV0d29yaycsIHJlcHJlc2VudGluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gdXNlcnMgd2hvIGhhdmUgaW50ZXJhY3RlZCB3aXRoIGVhY2ggb3RoZXIuIEZvciBZb3VUdWJlIGNvbW1lbnQgdGhyZWFkcywgYSByZWxhdGlvbnNoaXAgaXMgZGVmaW5lZCBhcyB1c2VyIGkgJ3JlcGx5aW5nIHRvJyBvciAnbWVudGlvbmluZycgdXNlciBqIGluIGEgY29tbWVudC4gSW4gdGhpcyBuZXR3b3JrIHRoZSB2ZXJ0aWNlcyAoYS5rLmEgJ25vZGVzJykgcmVwcmVzZW50IFlvdVR1YmUgdXNlcnMgYW5kIHRoZSBlZGdlcyAoYS5rLmEgJ2xpbmtzJykgcmVwcmVzZW50IHdoZXRoZXIgKGFuZCBob3cgbWFueSB0aW1lcykgdXNlciBpIGhhcyBpbnRlcmFjdGVkIHdpdGggdXNlciBqLiBUaGUgZWRnZXMgaW4gdGhpcyBuZXR3b3JrIGFyZSBib3RoIGRpcmVjdGVkIGFuZCB3ZWlnaHRlZC4gRWRnZXMgYXJlICdkaXJlY3RlZCcgYmVjYXVzZSBpbnRlcmFjdGlvbnMgbWF5IG5vdCBiZSByZWNpcHJvY2F0ZWQgKGUuZy4gdXNlciBpIHJlcGxpZXMgdG8gdXNlciBqLCBidXQgdXNlciBqIGRvZXMgbm90IHJlcGx5IHRvIHVzZXIgaSksIGFuZCBlZGdlcyBhcmUgYWxzbyDigJh3ZWlnaHRlZOKAmSBpbiBvcmRlciB0byBzaG93IGhvdyBtYW55IHRpbWVzIHVzZXIgaSBoYXMgaW50ZXJhY3RlZCB3aXRoIHVzZXIgai4KYGBge3J9CnJlcXVpcmUoaWdyYXBoKQpnX2FjdG9yX3lvdXR1YmUgPC0gbXlZb3V0dWJlRGF0YSAlPiUgQ3JlYXRlKCJBY3RvciIpCmBgYAoKV2UgY2FuIG5vdyB2aWV3IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IG91ciBuZXR3b3JrLCBub3RhYmx5IHRoZSBudW1iZXIgb2YgdmVydGljZXMgKHVzZXJzKSBhbmQgbnVtYmVyIG9mIGludGVyYWN0aW9ucyAoZWRnZXMpOgpgYGB7cn0KZ19hY3Rvcl95b3V0dWJlCmBgYAoKCiMjIFN0b3JpbmcgeW91ciBzb2NpYWwgbmV0d29yayBpbiBncmFwaG1sIGZvcm1hdAoKWW91IGNhbiBleHBvcnQgYSBzb2NpYWwgbmV0d29yayBnZW5lcmF0ZWQgYnkgU29jaWFsTWVkaWFMYWIgdG8gImdyYXBobWwiIGZvcm1hdCB1c2luZyBpZ3JhcGggcGFja2FnZToKCmBgYHtyfQpyZXF1aXJlKGlncmFwaCkgIyBpbnN0YWxsLnBhY2thZ2VzKCJpZ3JhcGgiKSBpZiBub3QgaW5zdGFsbGVkIHlldApgYGAKCk5vdyB5b3UgY2FuIHZpc3VhbGl6ZSB0aGUgImdyYXBobWwiIGZpbGUgd2l0aCBHZWxwaGkgb3Igb3RoZXIgY29tcGF0aWFibGUgdG9vbHMuCmBgYHtyfQp3cml0ZS5ncmFwaChnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrLCAiZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlay5ncmFwaG1sIiwgZm9ybWF0PSJncmFwaG1sIikKYGBgCgojIFNvY2lhbCBOZXR3b3JrIEFuYWx5c2lzCgojIyBCYXNpYyBjb3VudGluZwoKTmV4dCB3ZSB3aWxsIGRvIHNvbWUgbW9yZSBkZXNjcmlwdGl2ZSBhbmFseXNpcy4KCkhvdyBtYW55IG5vZGVzIGFyZSBpbiB0aGUgbmV0d29yaz8KYGBge3J9CnZjb3VudChnX2JpbW9kYWxfdHdpdHRlcikKYGBgCgpIb3cgbWFueSBlZGdlcyBpbiB0aGUgbmV0d29yaz8KYGBge3J9CmVjb3VudChnX2JpbW9kYWxfdHdpdHRlcikKYGBgCgpHZXQgYSBsaXN0IG9mIHRoZSBub2RlcyBpbiB0aGUgbmV0d29yazoKYGBge3J9ClYoZ19iaW1vZGFsX3R3aXR0ZXIpCmBgYAoKTGlzdCBvZiBlZGdlcyBpbiB0aGUgbmV0d29yazoKYGBge3J9CkUoZ19iaW1vZGFsX3R3aXR0ZXIpCmBgYAoKQWNjZXNzIGEgcGFydGljdWxhciBub2RlIGluIHRoZSBuZXR3b3JrIChub2RlICM0Mik6CmBgYHtyfQpWKGdfYmltb2RhbF90d2l0dGVyKVs0Ml0KYGBgCgpBY2Nlc3MgYSBwYXJ0aWN1bGFyIGVkZ2VzOgpgYGB7cn0KRShnX2JpbW9kYWxfdHdpdHRlcilbMV0KYGBgCgojIyBHcmFwaCBjb25uZWN0aXZpdHkKCkxvb2sgYXQgdGhlIGNvbm5lY3Rpdml0eSBvZiB0aGUgZ3JhcGg6CmBgYHtyfQojIHdobyBhcmUgdGhlIG5laWdoYm91cnMgb2Ygbm9kZSAjNDI/Cm5laWdoYm9ycyhnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzLDQyKQojdGhpcyBpcyBub3QgYSB3ZWFrbHkgY29ubmVjdGVkIGNvbXBvbmVudAppcy5jb25uZWN0ZWQoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfd2FycywgbW9kZT0id2VhayIpCiNpbmZvcm1hdGlvbiBvbiBjb25uZWN0ZWQgY29tcG9uZW50cwpjYyA8LSBjbHVzdGVycyhnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzKQojd2hpY2ggY29tcG9uZW50IG5vZGUgaXMgYXNzaWduZWQgdG8KIyBjYyRtZW1iZXJzaGlwCiNzaXplIG9mIGVhY2ggY29tcG9uZW50CmNjJGNzaXplCiNudW1iZXIgb2YgY29tcG9uZW50cwpjYyRubwoKI3N1Ym5ldHdvcmsgLSBnaWFudCBjb21wb25lbnQKZzMgPC0gaW5kdWNlZF9zdWJncmFwaChnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl93YXJzLCB3aGljaChjYyRtZW1iZXJzaGlwID09IHdoaWNoLm1heChjYyRjc2l6ZSkpKQpgYGAKCldlIHdpbGwgbm93IGxvb2sgYXQgbm9kZSBjZW50cmFsaXR5OgpgYGB7ciBldmFsPUZBTFNFfQojbm9kZSBpbmRlZ3JlZQpkZWdyZWUoZzMsIG1vZGU9ImluIikKI25vZGUgb3V0ZGVncmVlCmRlZ3JlZShnMywgbW9kZT0ib3V0IikKI25vZGUgaW5kZWdyZWUsIHVzaW5nIGVkZ2Ugd2VpZ2h0cwppbmQgPC0gc3RyZW5ndGgoZzMsIG1vZGU9ImluIikKI3RvcC01IG5vZGVzLCBiYXNlZCBvbiAod2VpZ2h0ZWQpIGluZGVncmVlClYoZzMpW29yZGVyKGluZCwgZGVjcmVhc2luZz1UKVsxOjNdXQojY2xvc2VuZXNzIGNlbnRyYWxpdHkKY2xvc2VuZXNzKGczKQojYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpiZXR3ZWVubmVzcyhnMykKI2VpZ2VudmVjdG9yIGNlbnRyYWxpdHkKZXZjZW50KGczKSR2ZWN0b3IKYGBgCgoKV2UgY2FuIGxvb2sgYXQgc29tZSBuZXR3b3JrIGNvaGVzaW9uIG1lYXN1cmVzLiBIb3cgZGVuc2UgaXMgdGhlIGdyYXBoPyBJbiBvdGhlciB3b3Jkcywgb2YgYWxsIHRoZSBwb3NzaWJsZSBjb25uZWN0aW9ucyBiZXR3ZWVuIG5vZGVzLCBob3cgbWFueSBhcmUgYWN0dWFsbHkKb2JzZXJ2ZWQ/CmBgYHtyfQojIGRlbnNpdHkKZ3JhcGguZGVuc2l0eShnMykKCiMgKGdsb2JhbCkgY2x1c3RlcmluZyBjb2VmZmljaWVudAojIHJlbC4gZnJlcXVlbmN5IGNvbm5lY3RlZCB0cmlwbGVzIGNsb3NlIHRvIGZvcm0gdHJpYW5nbGVzCnRyYW5zaXRpdml0eShnMykKCiMgbnVtYmVyIG9mIGR5YWRzIHdpdGggcmVjaXByb2NhdGVkIChtdXR1YWwpCiMgZWRnZXMvbnVtYmVyIG9mIGR5YWRzIHdpdGggc2luZ2xlIGVkZ2UKcmVjaXByb2NpdHkoZzMsIG1vZGU9ImRlZmF1bHQiKQoKI3RvdGFsIG51bWJlciBvZiByZWNpcHJvY2F0ZWQgZWRnZXMvdG90YWwgbnVtYmVyIG9mIGVkZ2VzCnJlY2lwcm9jaXR5KGczLCBtb2RlPSJyYXRpbyIpCmBgYAoKIyMgRmluZCBpbXBvcnRhbnQgbm9kZXMgaW4gdGhlIG5ldHdvcmsKCldobyBhcmUgdGhlIHRvcCAzIG1vc3QgaW1wb3J0YW50IHBvc3RzIGluIHRoZSBGYWNlYm9vayBuZXR3b3JrPyBUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGRvIHRoaXMuIEZvciBmdW4gd2Ugd2lsbAp1c2UgdGhlIFBhZ2VSYW5rIGFsZ29yaXRobSBpbXBsZW1lbnRhdGlvbiBpbiBpZ3JhcGggdG8gY2FsY3VsYXRlIHRoaXMuIFBhZ2VSYW5rIGlzIG1hZGUgZmFtb3VzIGJ5IHRoZQpHb29nbGUgY28tZm91bmRlcnMsIHdobyBpbnZlbnRlZCB0aGlzIG1ldGhvZCB0byBkZXRlcm1pbmUgdGhlIGltcG9ydGFuY2Ugb2Ygd2VicGFnZXMsIHJldm9sdXRpb25pc2luZyB0aGUKc2VhcmNoIGVuZ2luZSBpbmR1c3RyeS4gVGhlIGZvbGxvd2luZyBjb2RlIGNhbGN1bGF0ZXMgUGFnZVJhbmsgZm9yIG5vZGVzIGluIHRoZSBuZXR3b3JrLCBhbmQgcmV0dXJucyB0aGUgMwondG9wJyBub2RlcyAod2hpY2ggaGF2ZSB0aGUgaGlnaGVzdCBzaGFyZSBvZiBQYWdlUmFuayksIHByb3ZpZGluZyB0aGUgSUQuCmBgYHtyfQpwYWdlcmFua19pbnN0YWdyYW0gPC0gc29ydChwYWdlLnJhbmsoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfd2FycykkdmVjdG9yLGRlY3JlYXNpbmc9VFJVRSkKaGVhZChwYWdlcmFua19pbnN0YWdyYW0sbj0zKQpgYGAKCldoYXQgYXJlIHRoZSB0b3AgMTAgaW1wb3J0YW50IHRlcm1zIGluIG91ciAjYXVzcG9sIGFjdG9yIG5ldHdvcms/IFRoZXJlIGlzIG5vIHJlYXNvbiB3aHkgd2UgY2FuJ3QgdXNlCnRoZSBQYWdlUmFuayBhbGdvcml0aG0gdG8gY2FsY3VsYXRlIHRoaXMgKGFzIHBlciB0aGUgSW5zdGFncmFtIGFuYWx5c2lzIHByZXZpb3VzbHkpOgpgYGB7cn0KcGFnZVJhbmtfYXVzcG9sX3NlbWFudGljIDwtIHNvcnQocGFnZS5yYW5rKGdfdHdpdHRlcl9zZW1hbnRpYykkdmVjdG9yLGRlY3JlYXNpbmc9VFJVRSkKaGVhZChwYWdlUmFua19hdXNwb2xfc2VtYW50aWMsbj0xMCkKYGBgCgpXaGF0IGFib3V0IHRoZSAzIGxlYXN0IGltcG9ydGFudCB1c2VycyAod2l0aCBhbGwgZHVlIHJlc3BlY3QuLi4pOgpgYGB7cn0KdGFpbChwYWdlUmFua19hdXNwb2xfc2VtYW50aWMsbj0zKQpgYGAKCk9idmlvdXNseSB0aGUgI2F1c3BvbCBoYXNodGFnIGlzIGdvaW5nIHRvIGJlIHRoZSBtb3N0IGltcG9ydGFudCBiZWNhdXNlIGl0IG9jY3VycyBhdCBsZWFzdCBvbmNlIGluIGV2ZXJ5CnR3ZWV0LiBXZSBjYW4gYWN0dWFsbHkgYXZvaWQgdGhpcyBieSB1c2luZyB0aGUgcmVtb3ZlVGVybXNPckhhc2h0YWdzIGFyZ3VtZW50IHdoZW4gd2UgQ3JlYXRlKCkgdGhlCm5ldHdvcmsuIFRoaXMgYXJndW1lbnQgc3BlY2lmaWVzIHdoaWNoIHRlcm1zIG9yIGhhc2h0YWdzIChpLmUuIG5vZGVzIHdpdGggYSBuYW1lIHRoYXQgbWF0Y2hlcyBvbmUgb3IgbW9yZQp0ZXJtcykgc2hvdWxkIGJlIHJlbW92ZWQgZnJvbSB0aGUgc2VtYW50aWMgbmV0d29yay4gVGhpcyBpcyB1c2VmdWwgdG8gcmVtb3ZlIHRoZSBzZWFyY2ggdGVybSBvciBoYXNodGFnCnRoYXQgd2FzIHVzZWQgdG8gY29sbGVjdCB0aGUgZGF0YSAoaS5lLiByZW1vdmUgdGhlIGNvcnJlc3BvbmRpbmcgbm9kZSBpbiB0aGUgZ3JhcGgpLiBGb3IgZXhhbXBsZSwgYSB2YWx1ZSBvZgoiI2F1c3BvbCIgbWVhbnMgdGhhdCB0aGUgbm9kZSB3aXRoIHRoZSBuYW1lICIjYXVzcG9sIiB3aWxsIGJlIHJlbW92ZWQuIE5vdGU6IHlvdSBjb3VsZCBhbHNvIGp1c3QgZGVsZXRlCnRoZSAjYXVzcG9sIG5vZGUgbWFudWFsbHkuCgpBbm90aGVyIGtleSBhc3BlY3Qgb2Ygc2VtYW50aWMgbmV0d29ya3MgaXMgaG93IG1hbnkgdGVybXMgdG8gaW5jbHVkZSBpbiB0aGUgbmV0d29yay4gQnkgZGVmYXVsdCwKU29jaWFsTWVkaWFMYWIgZG9lcyBub3QgaW5jbHVkZSBldmVyeSB1bmlxdWUgdGVybSB0aGF0IGl0IGZpbmRzIGluIHRoZSB0d2VldHMsIGJ1dCBvbmx5IHRoZSA1IHBlcmNlbnQgbW9zdApmcmVxdWVudGx5IG9jY3VycmluZyB0ZXJtcy4gWW91IGNhbiBjaGFuZ2UgdGhpcyB3aGVuIGNhbGxpbmcgdGhlIENyZWF0ZSgpIG5ldHdvcmsgZnVuY3Rpb24sIGZvciBleGFtcGxlIGJ5CnNwZWNpZnlpbmcgYSB2YWx1ZSA1MCAobWVhbmluZyB0aGF0IHRoZSA1MCBwZXJjZW50IG1vc3QgZnJlcXVlbnRseSBvY2N1cnJpbmcgdGVybXMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUKc2VtYW50aWMgbmV0d29yaykuCgpXZSBjYW4gYWN0dWFsbHkgdHJ5IHRoaXMgb3V0IG5vdy4gV2Ugd2lsbCBjcmVhdGUgYW5vdGhlciBzZW1hbnRpYyBuZXR3b3JrLCBidXQgd2Ugd2lsbCBleGNsdWRlIHRoZSAjYXVzcG9sCmhhc2h0YWcsIGFuZCB3ZSB3aWxsIGluY2x1ZGUgZXZlcnkgc2luZ2xlIHRlcm0gYXZhaWxhYmxlIGluIHRoZSB0d2VldHMuCmBgYHtyfQpnX3R3aXR0ZXJfc2VtYW50aWNfYXVzcG9sX2FsbFRlcm1zIDwtIG15VHdpdHRlckRhdGEgJT4lIENyZWF0ZSgiU2VtYW50aWMiLCB0ZXJtRnJlcT0xMDAsIHJlbW92ZVRlcm1zT3JIYXNodGFncz1jKCIjYXVzcG9sIikpCmBgYAoKVGhlIHNpemUgb2YgdGhlIG5ldHdvcmsgd2lsbCBpbmNyZWFzZSBhIGxvdCwgZXZlbiBpbiB0aGUgYWJzZW5jZSBvZiB0aGUgI2F1c3BvbCB0ZXJtIQpgYGB7cn0KZ190d2l0dGVyX3NlbWFudGljX2F1c3BvbF9hbGxUZXJtcwpgYGAKCldoYXQgYXJlIHRoZSB0b3AgMTAgaW1wb3J0YW50IHRlcm1zIGluIG91ciBzZW1hbnRpYyBuZXR3b3JrIG5vdz8gT25jZSBhZ2FpbiB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGlzIHVzaW5nIFBhZ2VSYW5rOgpgYGB7cn0KcGFnZVJhbmtfYXVzcG9sX3NlbWFudGljX3JlcGxpY2F0ZSA8LSBzb3J0KHBhZ2UucmFuayhnX3R3aXR0ZXJfc2VtYW50aWNfYXVzcG9sX2FsbFRlcm1zKSR2ZWN0b3IsZGVjcmVhc2luZz1UUlVFKQpwYWdlUmFua19hdXNwb2xfc2VtYW50aWNfcmVwbGljYXRlWzE6MTBdCmBgYAoKCiMjIEZpbmQgY29tbXVuaXRpZXMKCklzIHRoZXJlIGFueSBraW5kIG9mIGNvbW11bml0eSBzdHJ1Y3R1cmUgd2l0aGluIHRoZSB1c2VyIG5ldHdvcms/IFdlIHdpbGwgdXNlIHRoZSBpbmZvbWFwIGFsZ29yaXRobSBpbXBsZW1lbnRhdGlvbiBpbiBpZ3JhcGguCmBgYHtyIHRpZHk9VFJVRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShpZ3JhcGgpCiMgaW5jcmVhc2UgbmIudHJpYWxzIGZvciBiZXR0ZXIgcXVhbGl0eSBjb21tdW5pdGllcwppbWMgPC0gaW5mb21hcC5jb21tdW5pdHkoZ190d2l0dGVyX2FjdG9yLCBuYi50cmlhbHMgPSAxMCkgCgojIGNyZWF0ZSBhIHZlY3RvciBvZiB1c2VycyB3aXRoIHRoZWlyIGFzc2lnbmVkIGNvbW11bml0eSBudW1iZXIKY29tbXVuaXR5TWVtYmVyc2hpcF9hdXNwb2wgPC0gbWVtYmVyc2hpcChpbWMpCgojIHN1bW1hcmlzZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHVzZXJzIHRvIGNvbW11bml0aWVzIApjb21tRGlzdHJpYnV0aW9uIDwtIHN1bW1hcnkoYXMuZmFjdG9yKGNvbW11bml0eU1lbWJlcnNoaXBfYXVzcG9sKSkgCgojIHdoaWNoIGNvbW11bml0eSBoYXMgdGhlIG1heCBudW1iZXIgb2YgdXNlcnMgCnRhaWwoc29ydChjb21tRGlzdHJpYnV0aW9uKSxuPTEpCmBgYAoKTG9vayBpbnRvIHRoZSBtZW1iZXJzIG9mIGVhY2ggY29tbXVuaXR5OgpgYGB7cn0KIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW11bml0aWVzIHRoYXQgaW5jbHVkZXMgdGhlIHVzZXJzIGFzc2lnbmVkIHRvIGVhY2ggY29tbXVuaXR5CmNvbW11bml0aWVzX2F1c3BvbCA8LSBjb21tdW5pdGllcyhpbWMpCgojIGxvb2sgYXQgdGhlIG1lbWJlcnMgb2YgdGhlIG1vc3QgcG9wdWxhdGVkIGNvbW11bml0eSAKY29tbXVuaXRpZXNfYXVzcG9sW25hbWVzKHRhaWwoc29ydChjb21tRGlzdHJpYnV0aW9uKSxuPTEpKV0KYGBgCgojIyBHcmFwaCBQcm9qZWN0aW9uCgpBbm90aGVyIHVzZWZ1bCB0ZWNobmlxdWUgd2UgY2FuIGRvIGlzIHRvIHBlcmZvcm0gYSBwcm9qZWN0aW9uIG9mIHRoZSBGYWNlYm9vayBuZXR3b3JrcyB3ZSBqdXN0IGNyZWF0ZWQuIFRoZXNlIG5ldHdvcmtzIGFyZSBiaXBhcnRpdGUgYmVjYXVzZSBub2RlcyBvZiB0aGUgc2FtZSB0eXBlIGNhbm5vdCBzaGFyZSBhbiBlZGdlIChlLmcuIGEgdXNlciBjYW4gb25seSBsaWtlL2NvbW1lbnQgb24gYSBwb3N0LCBidXQgbm90IGxpa2UvY29tbWVudCBhbm90aGVyIHVzZXIsIGFuZCBwb3N0cyBjYW5ub3QgcGVyZm9ybSBkaXJlY3RlZCBhY3Rpb25zIGVpdGhlciBvbiB1c2VycyBvciBvdGhlciBwb3N0cykuCgpXaGF0IHdlIGNhbiBkbyBpcyBpbmR1Y2UgdHdvIHN1YmdyYXBocyBmcm9tIGVhY2ggbmV0d29yay4gTW9yZSBzcGVjaWZpY2FsbHksIHdlIGNhbiBpbmR1Y2UgdHdvIGFjdG9yIG5ldHdvcmtzLCBvbmUgZm9yIHRoZSB1c2VycyBhbmQgb25lIGZvciB0aGUgcG9zdHMuCmBgYHtyfQojIyBzb21lIGRhdGEgcHJlcGFyYXRpb24KIyBjb2VyY2UgdG8gZmFjdG9yCmdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbiA8LSBnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrClYoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKSR0eXBlIDwtIGFzLmZhY3RvcihWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbikkdHlwZSkKCiMgY29lcmNlIGFsbCBwb3N0cyAoaS5lLiAiMSIpIHRvIGxvZ2ljYWwgKGkuZS4gRkFMU0UpClYoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKSR0eXBlW3doaWNoKFYoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKSR0eXBlPT0iMSIpXSA8LSBhcy5sb2dpY2FsKEZBTFNFKQoKIyBjb2VyY2UgYWxsIHVzZXJzIChpLmUuICIyIikgdG8gbG9naWNhbCAoaS5lLiBUUlVFKQpWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbikkdHlwZVt3aGljaChWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtfcHJvamVjdGlvbikkdHlwZT09IjIiKV0gPC0gYXMubG9naWNhbChUUlVFKQoKIyBub3cgcHJvamVjdCB0aGUgbmV0d29yawpwcm9qZWN0aW9uX2dfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWsgPC0gYmlwYXJ0aXRlLnByb2plY3Rpb24oZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla19wcm9qZWN0aW9uKQpgYGAKCkZpcnN0bHksIHdlIHdpbGwgbG9vayBhdCB0aGUgaW5kdWNlZCBncmFwaCBmb3IgdGhlICJwb3N0cyIuIFRoZSBpbmR1Y2VkICJwb3N0cyIiIGFjdG9yIG5ldHdvcmsgY29uc2lzdHMgb25seSBvZiBub2RlcyB0aGF0IGFyZSBvZiB0eXBlICJwb3N0Ii4gQW4gZWRnZSBleGlzdHMgYmV0d2VlbiBwb3N0IGkgYW5kIHBvc3QgaiBpZiB0aGV5IGFyZSBib3RoIGNvLWxpa2VkIG9yIGNvLWNvbW1lbnRlZCBieSB0aGUgc2FtZSB1c2VyIChpLmUuIGlmIHRoZXkgaGF2ZSBhbnkgdXNlciBpbiBjb21tb24pLiBOb3Qgc3VycHJpc2luZ2x5LCBldmVyeSBwb3N0IGhhcyBhdCBsZWFzdCBvbmUgdXNlciBpbiBjb21tb24sIHdoaWNoIHJlc3VsdHMgaW4gdGhlIG5ldHdvcmsgYmVpbmcgImNvbXBsZXRlIi4KYGBge3IgdGlkeT1UUlVFfQpwcm9qZWN0aW9uX2dfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWtbWzFdXQojIHBuZygiZmFjZWJvb2tfc3Rhcl90cmVrX3Bvc3RzLnBuZyIsIHdpZHRoPTgwMCwgaGVpZ2h0PTcwMCkgCnBsb3QocHJvamVjdGlvbl9nX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrW1sxXV0sIGVkZ2Uud2lkdGg9MS41LCBlZGdlLmN1cnZlZD0wLjUsIGVkZ2UuYXJyb3cuc2l6ZT0wLjUpICN2ZXJ0ZXguc2hhcGU9Im5vbmUiLAojIGRldi5vZmYoKQpgYGAKClNlY29uZGx5LCB3ZSB3aWxsIGxvb2sgYXQgdGhlIGluZHVjZWQgZ3JhcGggZm9yIHRoZSDigJx1c2Vyc+KAnS4gVGhlIGluZHVjZWQg4oCcdXNlcnPigJ0gYWN0b3IgbmV0d29yayBjb25zaXN0cyBvbmx5IG9mIG5vZGVzIHRoYXQgYXJlIG9mIHR5cGUg4oCcdXNlcuKAnS4gQW4gZWRnZSBleGlzdHMgYmV0d2VlbiB1c2VyIGkgYW5kIHVzZXIgaiBpZiB0aGV5IGJvdGggY28tbGlrZWQgb3IgY28tY29tbWVudGVkIHRoZSBzYW1lIHBvc3QgKGkuZS4gdGhleSBzaGFyZSBhbiBpbnRlcmFjdGlvbiB3aXRoIGEgcG9zdCBqKS4gQXMgeW91IG1pZ2h0IGV4cGVjdCwgdGhpcyBjcmVhdGUgYSBuZXR3b3JrIHdpdGggYSBtYXNzaXZlIG51bWJlciBvZiBlZGdlcyEgQSBsb3Qgb2YgdXNlcnMgY28taW50ZXJhY3Qgd2l0aCB0aGUgc2FtZSBwb3N0cy4gRm9yIHRoaXMgZXhhbXBsZSwgb3ZlciA0LjUgbWlsbGlvbiBlZGdlcyAoeW91ciByZXN1bHRzIG1pZ2h0IGJlIHNvbWV3aGF0IGRpZmZlcmVudCkuCmBgYHtyfQojIHdhcm5pbmcgLSBkbyBub3QgdXNlIOKAmHN0cuKAmCBmdW5jdGlvbiBiZWNhdXNlIGl0IHdpbGwKIyBjYXVzZSBSIHRvIGZyZWV6ZSB1cCBkdWUgdG8gb3ZlcmxvYWRpbmcgdGhlIGNvbnNvbGUgb3V0cHV0IQojIEFsc286IHlvdSB3aWxsIHByb2JhYmx5IGhhdmUgZGlmZmljdWx0eSBwbG90dGluZyB0aGlzIGdyYXBoIGluIFIgYmVjYXVzZSBpdCBpcyBzbyBiaWcgCnByb2plY3Rpb25fZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJla1tbMl1dCmBgYAoKTWF5YmUgdGhlcmUgaXMgc29tZSBjb21tdW5pdHkgc3RydWN0dXJlIHRvIHRoaXMgbGFyZ2UgbmV0d29yay4gVGhlcmUgYXJlIHNldmVyYWwgd2F5cyB0byBmaW5kIG91dC4gV2Ugd2lsbCB1c2UgdGhlIGluZm9tYXAgYWxnb3JpdGhtIGltcGxlbWVudGF0aW9uIGluIGlncmFwaC4gSW5mb21hcCB1c2VzIGFuIGluZm9ybWF0aW9uIHRoZW9yZXRpYywgZmxvdy1iYXNlZCBhcHByb2FjaCB0byBjYWxjdWxhdGluZyBjb21tdW5pdHkgc3RydWN0dXJlIGluIG5ldHdvcmtzLiBJdCBzdXBwb3J0cyB3ZWlnaHRlZCBhbmQgZGlyZWN0ZWQgZ3JhcGhzLCBhbmQgYWxzbyBzY2FsZXMgd2VsbC4KClRoZSByZXN1bHRzIHNob3cgdGhhdCB0aGVyZSBpcyBkZWZpbml0ZWx5IHNvbWUgaW50ZXJlc3RpbmcgY29tbXVuaXR5IHN0cnVjdHVyZSB0byB0aGUgdXNlciBhY3RvciBuZXR3b3JrIChhIGhhbmRmdWwgb2YgbGFyZ2UgY29tbXVuaXRpZXMgYW5kIGEgdGlueSBjb21tdW5pdHkpLiBBbHRob3VnaCB5b3VyIHJlc3VsdHMgbWlnaHQgZGkgZXIsIGRlcGVuZGluZyBvbiB0aGUgYWN0dWFsIGRhdGEgY29sbGVjdGVkLgpgYGB7ciB0aWR5PVRSVUUsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9CiMgbGltaXQgdGhlIOKAmHRyaWFsc+KAmCBhcmd1bWVudCB0byBhIHNtYWxsIG51bWJlciB0byBzYXZlIHRpbWUgCiMgKG51bWJlciBvZiBhdHRlbXB0cyB0byBwYXJ0aXRpb24gdGhlIG5ldHdvcmspIAppbWNfc3RhcndhcnMgPC0gaW5mb21hcC5jb21tdW5pdHkocHJvamVjdGlvbl9nX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrW1syXV0sIG5iLnRyaWFscyA9IDMpCgojIGNyZWF0ZSBhIHZlY3RvciBvZiB1c2VycyB3aXRoIHRoZWlyIGFzc2lnbmVkIGNvbW11bml0eSBudW1iZXIKY29tbXVuaXR5TWVtYmVyc2hpcF9zdGFyd2FycyA8LSBtZW1iZXJzaGlwKGltY19zdGFyd2FycykgCgojIHN1bW1hcmlzZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHVzZXJzIHRvIGNvbW11bml0aWVzIApjb21tRGlzdHJpYnV0aW9uX3N0YXJ3YXJzIDwtIHN1bW1hcnkoYXMuZmFjdG9yKGNvbW11bml0eU1lbWJlcnNoaXBfc3RhcndhcnMpKQoKIyB3aGljaCBjb21tdW5pdHkgaGFzIHRoZSBtYXggbnVtYmVyIG9mIHVzZXJzIAp0YWlsKHNvcnQoY29tbURpc3RyaWJ1dGlvbl9zdGFyd2Fycyksbj0xKQoKIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW11bml0aWVzIHRoYXQgaW5jbHVkZXMgdGhlIHVzZXJzIGFzc2lnbmVkIHRvIGVhY2ggY29tbXVuaXR5CmNvbW11bml0aWVzX3N0YXJ3YXJzIDwtIGNvbW11bml0aWVzKGltY19zdGFyd2FycykKCiMgbG9vayBhdCB0aGUgbWVtYmVycyBvZiB0aGUgKmxlYXN0KiBwb3B1bGF0ZWQgY29tbXVuaXR5IApjb21tdW5pdGllc19zdGFyd2Fyc1tuYW1lcyhoZWFkKHNvcnQoY29tbURpc3RyaWJ1dGlvbl9zdGFyd2Fycyksbj0xKSldCmBgYAoKIyBUZXh0IEFuYWx5c2lzCgpOZXh0LCB3ZSB3aWxsIGRvIHNvbWUgZGVzY3JpcHRpdmUgdGV4dCBhbmFseXNpcyBvZiB0aGUgU3RhciBXYXJzIGZhbiBjb21tZW50cy4KClRPRE8KCiMjIERhdGEgcHJlLXByb2Nlc3NpbmcKCldlIGp1c3Qgd2FudCB0byBrZWVwIHRoZSBjaGFyYWN0ZXIgdmVjdG9yIG9mIOKAmGNvbW1lbnRz4oCZIGRhdGEsIGZvciBvdXIgcHVycG9zZXMgaW4gdGhpcyBzZXNzaW9uOgpgYGB7cn0KZmJEYXRhIDwtIG15U3RhcldhcnNEYXRhJGNvbW1lbnRUZXh0CmBgYAoKV2Ugb25seSB3YW50IGVsZW1lbnRzIG9mIGZiRGF0YSB0aGF0IGNvbnRhaW4gY29tbWVudCB0ZXh0IChtYW55IHJvd3Mgb2Ygb3VyIEZhY2Vib29rIGRhdGEgcmVwcmVzZW50CuKAmGxpa2Vz4oCZLCByYXRoZXIgdGhhbiDigJhjb21tZW50c+KAmSkuIFNvIHdlIHJlbW92ZSBhbnkgdGV4dCBkYXRhIHRoYXQgZXF1YWxzIOKAnE5vdF9hcHBsaWNhYmxl4oCdICh0aGlzIGlzIGhvdwpTb2NpYWxNZWRpYUxhYiBkZXNpZ25hdGVzIHJvd3MgaW4gdGhlIGRhdGFmcmFtZSB0aGF0IGFyZSDigJhsaWtlc+KAmSkuIE5vdGU6IGluIGVhcmxpZXIgdmVyc2lvbnMgb2YgU29jaWFsTWVkaWFMYWIKdGhlc2UgZWxlbWVudHMgd2VyZSBkZXNpZ25hdGVkIGFzIE5BLCBob3dldmVyIHRoaXMgY2F1c2VkIHVuaW50ZW5kZWQgY29uc2VxdWVuY2VzIHNvIGl0IHdhcyBjaGFuZ2VkLgpgYGB7cn0KdG9SZW1vdmUgPC0gd2hpY2goZmJEYXRhPT0iTm90X2FwcGxpY2FibGUiKQpmYkRhdGEgPC0gZmJEYXRhWy10b1JlbW92ZV0gIyByZW1vdmUgdGhlIGVsZW1lbnRzIHdlIHdhbnQgdG8gZXhjbHVkZQpgYGAKCkhvdyBtYW55IGNvbW1lbnRzIGRvIHdlIGhhdmUgbGVmdCBub3c/CmBgYHtyfQpsZW5ndGgoZmJEYXRhKQpgYGAKCldlIGNvbnZlcnQgdGhlIGNoYXJhY3RlciBlbmNvZGluZyB0byBVVEYtOC4gVGhpcyBhdm9pZHMgZXJyb3JzIHJlbGF0aW5nIHRvIOKAmG9kZOKAmSBjaGFyYWN0ZXJzIGluIHRoZSB0ZXh0LiBUaGlzCmlzIHVzdWFsbHkgYSBnb29kIGlkZWEsIGJ1dCB0aGVyZSBtYXkgYmUgc2l0dWF0aW9ucyB3aGVuIGl0IGlzIG5vdCB1c2VmdWwsIG9yIGV2ZW4gZGV0cmltZW50YWwuIE5vdGU6IE1hYyB1c2VycwptYXkgZW5jb3VudGVyIGVycm9ycy9idWdzIHJlbGF0aW5nIHRvIGNoYXJhY3RlciBlbmNvZGluZywgYW5kIGEgd29ya2Fyb3VuZCBpcyB0byBjb252ZXJ0IHRvIOKAmHV0Zi04LW1hY+KAmToKYGBge3J9CmZiRGF0YSA8LSBpY29udihmYkRhdGEsIHRvID0gInV0Zi04IikKIyAqKk1BQyBVU0VSUyBPTkxZKiogc2hvdWxkIHVzZSB0aGlzIGluc3RlYWQ6CmZiRGF0YSA8LSBpY29udihmYkRhdGEsdG89InV0Zi04LW1hYyIpCmBgYAoKV2UgY29udmVydCBvdXIgY2hhcmFjdGVyIHZlY3RvciBmYkRhdGEgdG8gYSBWY29ycHVzIG9iamVjdDoKYGBge3Igd2FybmluZz1GQUxTRX0KbGlicmFyeSh0bSkKZmJDb3JwdXMgPC0gVkNvcnB1cyhWZWN0b3JTb3VyY2UoZmJEYXRhKSkKYGBgCgpJbmRpdmlkdWFsIGNvbW1lbnRzIChhLmsuYS4g4oCYZG9jdW1lbnRz4oCZKSBjYW4gYmUgYWNjZXNzZWQgdmlhIHRoZSBkb3VibGUgYnJhY2tldHMgbm90YXRpb24gb3IgdGhlIOKAmGRvbGxhcgpzaWdu4oCZIG5vdGF0aW9uIGZvciBhY2Nlc3NpbmcgbGlzdCBlbGVtZW50cy4gTGV04oCZcyBsb29rIGF0IGNvbW1lbnQgIzQuCmBgYHtyfQpmYkNvcnB1c1tbNF1dW1sxXV0KIyBhbm90aGVyIHdheSB0byBhY2Nlc3MgaXQKZmJDb3JwdXNbWzRdXSRjb250ZW50CmBgYAoKV2UgY2FuIHBlcmZvcm0gYSBudW1iZXIgb2YgaGlnaGx5IHVzZWZ1bCB0cmFuc2Zvcm1hdGlvbnMgb2YgdGV4dCB1c2luZyB0bV9tYXAgZnVuY3Rpb24gKGkuZS4g4oCYbWFwcGluZyB0bwp0aGUgY29ycHVz4oCZKS4gTm90IGFsbCBvZiB0aGVzZSB0cmFuc2Zvcm1hdGlvbnMgYXJlIHVzZWZ1bCBpbiBldmVyeSBzY2VuYXJpbyEgVGhleSBzaG91bGQgYmUgdXNlZCBvbmx5IHdoZW4gaXQKbWFrZXMgc2Vuc2UsIG9yIGFzIHJlcXVpcmVkLCBldGMuCgpDb252ZXJ0aW5nIGFsbCB0aGUgdGV4dCB0byBsb3dlcmNhc2U6CmBgYHtyfQpmYkNvcnB1cyA8LSB0bV9tYXAoZmJDb3JwdXMsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCmBgYAoKUmVtb3ZlIG51bWJlcnMgZnJvbSB0aGUgdGV4dDoKYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlTnVtYmVycykKYGBgCgpSZW1vdmUgcHVuY3R1YXRpb24gZnJvbSB0aGUgdGV4dDoKYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pCmBgYAoKUGVyZm9ybSDigJh3b3JkIHN0ZW1taW5n4oCZIG9uIHRoZSB0ZXh0LiBOb3RlOiB0aGlzIHRyYW5zZm9ybWF0aW9uIGNhbiBiZSBoaWdobHkgdXNlZnVsLCBidXQgYWxzbyBoaWdobHkKZGV0cmltZW50YWwhCmBgYHtyfQojIHVzZSBsYXp5PVRSVUUgYXJndW1lbnQgdG8gYXZvaWQgd2FybmluZyBvbiBzb21lIG1hY2hpbmVzIHdpdGggbXVsdGlwbGUgQ1BVIGNvcmVzCmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgc3RlbURvY3VtZW50LGxhenk9VFJVRSkKYGBgCgpXZSBjYW4gYWxzbyByZW1vdmUgRW5nbGlzaCDigJhzdG9wIHdvcmRz4oCZIGZyb20gdGhlIHRleHQuIFRoZXNlIGFyZSBjb21tb24gd29yZHMgKGUuZy4g4oCYdGhl4oCZLCDigJhhbmTigJksIOKAmG9y4oCZKSB0aGF0CndlIG1heSB3YW50IHRvIGV4Y2x1ZGUgZnJvbSBvdXIgYW5hbHlzaXMuIE9uY2UgYWdhaW4sIHRoaXMgaXMgaGlnaGx5IHVzZWZ1bCBidXQgYWxzbyBuZWVkcyB0byBiZSBjYXJlZnVsbHkKYXBwbGllZC4KYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpLGxhenk9VFJVRSkgCiMgdXNlIGxhenk9VFJVRSBhcmd1bWVudCB0byBhdm9pZCB3YXJuaW5nIG9uIHNvbWUgbWFjaGluZXMgd2l0aCBtdWx0aXBsZSBDUFUgY29yZXMKYGBgCgpFbGltaW5hdGUgdW5uZWNlc3Nhcnkg4oCYd2hpdGUgc3BhY2XigJkgZnJvbSB0aGUgdGV4dC4gRm9yIGV4YW1wbGUsIOKAnGhlbGxvIGV2ZXJ5b25lIG15IG5hbWUgaXMgZnJlZOKAnSBiZWNvbWVzCuKAnGhlbGxvIGV2ZXJ5b25lIG15IG5hbWUgaXMgZnJlZOKAnToKYGBge3J9CmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgc3RyaXBXaGl0ZXNwYWNlLCBsYXp5PVRSVUUpCmBgYAoKV2UgY2FuIG9ic2VydmUgdGhlIGRpP2VyZW5jZSBub3cgYnkgZXhhbWluaW5nIGNvbW1lbnQgIzQgYWdhaW46CmBgYHtyfQpmYkNvcnB1c1tbNF1dJGNvbnRlbnQKYGBgCgpXZSBjb3VsZCBhbHNvIGRlZmluZSBvdXIgb3duIHN0b3Agd29yZHMgYW5kIHRyYW5zZm9ybSB0aGUgdGV4dCB1c2luZyB0aGVzZToKYGBge3J9Cm15U3RvcHdvcmRzIDwtIGMoImphciIsImJpbmtzIikKZmJDb3JwdXMgPC0gdG1fbWFwKGZiQ29ycHVzLCByZW1vdmVXb3JkcywgbXlTdG9wd29yZHMpCmBgYAoKCgojIyBGcmVxdWVuY3kgYW5hbHlzaXMKCk5leHQgd2UgY3JlYXRlIGEgZG9jdW1lbnQtdGVybSBtYXRyaXggKERUTSkgZnJvbSB0aGUgZmJDb3JwdXMgb2JqZWN0LiBEVE1zIGFyZSBhIHZlcnkgaW1wb3J0YW50CmNvbmNlcHQgZm9yIHRleHQgYW5hbHlzaXMgYW5kIGFyZSBoaWdobHkgdXNlZnVsLiBEVE1zIGNhbiBiZSB0aG91Z2h0IGFib3V0IGFzIGEgdGFibGUgKGkuZS4gbWF0cml4KSB3aGVyZQp0aGUgcm93cyBhcmUg4oCYZG9jdW1lbnRz4oCZIChpLmUuIEZhY2Vib29rIGNvbW1lbnRzIGluIG91ciBkYXRhc2V0KSwgYW5kIHRoZSBjb2x1bW5zIGFyZSDigJh0ZXJtc+KAmSAoaS5lLiBlYWNoCnVuaXF1ZSB3b3JkIGZvdW5kIGFjcm9zcyBhbGwgdGhlIGRvY3VtZW50cyBpbiB0aGUgZGF0YXNldCkuIFRoZSDigJhjZWxsc+KAmSAoaS5lLiBlbGVtZW50cykgb2YgdGhlIG1hdHJpeCBpbmRpY2F0ZQpob3cgbWFueSB0aW1lcyB0ZXJtIG4gb2NjdXJyZWQgaW4gZG9jdW1lbnQgbS4KCk5vdGU6IHdlIHVzZSB0aGUgY29udHJvbCBhcmd1bWVudCB0byBzcGVjaWZ5IHRoYXQgd2Ugb25seSB3YW50IHRvIHJldGFpbiB3b3JkcyB0aGF0IGFyZSBtaW5pbXVtIGNoYXJhY3RlcgpsZW5ndGggb2YgMywgdXAgdG8gYSBtYXhpbXVtIG9mIDIwIGNoYXJhY3RlcnMuCmBgYHtyfQpkdG0gPC0gRG9jdW1lbnRUZXJtTWF0cml4KGZiQ29ycHVzLGNvbnRyb2wgPSBsaXN0KHdvcmRMZW5ndGhzPWMoMywgMjApKSkKZHRtCmBgYAoKV2hhdCB3ZSBoYXZlIGlzIGEgc3BhcnNlIG1hdHJpeCwgaS5lLiBtb3N0IG9mIHRoZSBlbGVtZW50cyBvZiB0aGUgbWF0cml4IGFyZSAwLCBpLmUuIGluIG91ciBkYXRhc2V0IG1vc3QKRmFjZWJvb2sgY29tbWVudHMgY29udGFpbiBvbmx5IGEgc21hbGwgcGVyY2VudGFnZSBvZiDigJh2b2NhYnVsYXJ54oCZIG9mIHRlcm1zIG9ic2VydmVkIGFjcm9zcyB0aGUgZW50aXJlIHNldCBvZgpjb21tZW50cy4gV2hhdCB3ZSB3YW50IHRvIGRvIGlzIHJlbW92ZSB0ZXJtcyB0aGF0IG9jY3VyIHZlcnkgaW5mcmVxdWVudGx5LCB3aGljaCB3aWxsIGxlYXZlIHVzIHdpdGggdGhlCm1vc3Qg4oCYaW1wb3J0YW504oCZIHRlcm1zLiBXZSByZW1vdmUgc3BhcnNlIHRlcm1zIHVzaW5nIHRoZSByZW1vdmVTcGFyc2VUZXJtcyBmdW5jdGlvbiwgd2hpY2ggcmVtb3Zlcwp0ZXJtcyB0aGF0IG9jY3VyIGVxdWFsIHRvIG9yIGxlc3MgdGhhbiBhIHBlcmNlbnRhZ2UgdGhyZXNob2xkLgoKRm9yIGV4YW1wbGUsIGlmIHdlIHNldCBpdCB0byAwLjk5NSwgdGhlbiBhbGwgdGVybXMgdGhhdCBhcmUgYXQgbGVhc3QgOTkuNSUgc3BhcnNlIGFyZSByZW1vdmVkLiBUaGUgZm9sbG93aW5nCmNvbW1hbmQgbGV0cyB1cyDigJh0ZXN0IG91dOKAmSB3aGF0IG91ciBkb2N1bWVudC10ZXJtIG1hdHJpeCB3b3VsZCBsb29rIGxpa2UgaWYgd2Ugc2V0IHRoZSB0aHJlc2hvbGQgdG8gMC45OTU6CmBgYHtyfQpyZW1vdmVTcGFyc2VUZXJtcyhkdG0sIDAuOTk1KQpgYGAKCjAuOTk1IHdpbGwgZG8gdGhlIHRyaWNrIGZvciB1cyBpbiB0aGlzIHdvcmtzaG9wLCBzbyB3ZSB3aWxsIGNyZWF0ZSBhIG5ldyBkb2N1bWVudC10ZXJtIG1hdHJpeCB3aXRoIHRoaXMKdGhyZXNob2xkIGFwcGxpZWQgdG8gaXQ6CmBgYHtyfQpkdG1TcGFyc2VSZW1vdmVkIDwtIHJlbW92ZVNwYXJzZVRlcm1zKGR0bSwgMC45OTUpCmBgYAoKV2UgY2FuIGV4YW1pbmUgdGVybSBmcmVxdWVuY2llcyBpbiBvdXIgZGF0YS4gV2UgY3JlYXRlIGEgY2hhcmFjdGVyIHZlY3RvciBvZiB0aGUgc3VtcyBvZiBjb2x1bW5zIG9mIG91cgpkb2N1bWVudC10ZXJtIG1hdHJpeCAoaW1wbGljaXRseSBjb2VyY2luZyBpdCB0byBhIG1hdHJpeCBvYmplY3QpLCBtZWFuaW5nIHRoYXQgaGF2ZSBhIG5hbWVkIGNoYXJhY3Rlcgp2ZWN0b3Igd2hlcmUgdGhlIG5hbWVzIGFyZSB0aGUgdW5pcXVlIHRlcm1zIGluIG91ciBkb2N1bWVudC10ZXJtIG1hdHJpeCwgYW5kIHRoZSB2YWx1ZXMgb2YgdGhlIGVsZW1lbnRzCmFyZSB0aGUgbnVtYmVyIG9mIHRpbWVzIHRoYXQgcGFydGljdWxhciB3b3JkIG9jY3VycyBhY3Jvc3MgYWxsIG9mIG91ciBjb3JwdXMuCmBgYHtyfQpmcmVxVGVybXMgPC0gY29sU3Vtcyhhcy5tYXRyaXgoZHRtU3BhcnNlUmVtb3ZlZCkpCmZyZXFUZXJtcwpgYGAKCldlIG9yZGVyIHRoZSB0ZXJtIGZyZXF1ZW5jaWVzIGFuZCBsb29rIGF0IHRoZSA1IG1vc3QgZnJlcXVlbnQgdGVybXMgYW5kIHRoZW4gNSBsZWFzdCBmcmVxdWVudCB0ZXJtczoKYGBge3J9Cm9yZGVyVGVybXMgPC0gb3JkZXIoZnJlcVRlcm1zLGRlY3JlYXNpbmc9VFJVRSkKZnJlcVRlcm1zW2hlYWQob3JkZXJUZXJtcyldCmZyZXFUZXJtc1t0YWlsKG9yZGVyVGVybXMpXQpgYGAKCldoaWNoIHRlcm1zIG9jY3VycmVkIGF0IGxlYXN0IDIwIHRpbWVzPwpgYGB7cn0KZmluZEZyZXFUZXJtcyhkdG1TcGFyc2VSZW1vdmVkLCAyMCkKYGBgCgpXZSBjYW4gZG8gYSBiYXNpYyBjb3JyZWxhdGlvbiBhbmFseXNpcyBieSBsb29raW5nIGF0IHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0ZXJtcyB3aXRoIHRoZSBmaW5kQXNzb2NzCmZ1bmN0aW9uLiBJZiB0d28gd29yZHMgYWx3YXlzIGFwcGVhciB0b2dldGhlciB0aGVuIGNvcnIgPSAxLiBJZiB0d28gdGVybXMgbmV2ZXIgYXBwZWFyIHRvZ2V0aGVyIHRoZW4gY29yciA9CjAuIExldOKAmXMgbG9vayBhdCB3aGljaCB0ZXJtcyBjby1vY2N1ciB3aXRoIHRoZSB0ZXJtIOKAnG1lYXTigJ0sIHdpdGggYSBsb3dlciBjb3JyZWxhdGlvbiBsaW1pdCBvZiAwLjUuCmBgYHtyfQpmaW5kQXNzb2NzKGR0bVNwYXJzZVJlbW92ZWQsICJnb29kIiwgY29ybGltaXQ9MC41KQpgYGAKCk5leHQsIHdlIGNhbiBkbyBzb21lIHRleHQgdmlzdWFsaXNhdGlvbi4gRmlyc3QsIHdlIGNhbiBwbG90IG91ciBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGluIHZhcmlvdXMgd2F5cy4gRm9yCmV4YW1wbGUsIHVzaW5nIGEgYmFyY2hhcnQgdG8gdmlzdWFsaXNlIHRoZSAyMCBtb3N0IGZyZXF1ZW50IHRlcm1zICh3ZSB3aWxsIHVzZSB0aGUgbGF0dGljZSBwYWNrYWdlIGZvciBhCm5pY2UgYmFyIGNoYXJ0OgpgYGB7cn0KcmVxdWlyZShsYXR0aWNlKQojIHBuZygiYmFyY2hhcnRfZnJlcXVlbnRfdGVybXMucG5nIiwgd2lkdGg9ODAwLCBoZWlnaHQ9NzAwKQpiYXJjaGFydChmcmVxVGVybXNbb3JkZXJUZXJtc1sxOjIwXV0pCiMgZGV2Lm9mZigpCmBgYAoKCgojIyBXb3JkIENsb3VkCgpOZXh0LCB3ZSB3aWxsIGNvbnN0cnVjdCBhIGNvbXBhcmlzb24gd29yZCBjbG91ZCBvZiB0aGUgU3RhciBXYXJzIGFuZCBTdGFyIFRyZWsgZmFuIHBhZ2UgY29tbWVudHMuCmBgYHtyfQojIGNyZWF0ZSBhIGNoYXJhY3RlciB2ZWN0b3Igb2YgdGhlIFN0YXIgV2FycyBjb21tZW50cwojIChpLmUuLCB0YWtlIGEgc3Vic2V0IG9mIGVsZW1lbnRzIGZyb20gdGhlIGNvbW1lbnRUZXh0IGNvbHVtbiBvZiB0aGUgZGF0YWZyYW1lKQpzdGFyV2Fyc0NvbW1lbnRzIDwtIG15U3RhcldhcnNEYXRhJGNvbW1lbnRUZXh0W3doaWNoKG15U3RhcldhcnNEYXRhJGNvbW1lbnRUZXh0IT0iTm90X2FwcGxpY2FibGUiKV0Kc3RhcldhcnNDb21tZW50cyA8LSBwYXN0ZShzdGFyV2Fyc0NvbW1lbnRzICwgY29sbGFwc2UgPSAiICIpCgojIGRvIHRoZSBzYW1lLCBidXQgZm9yIFN0YXIgVHJlawpzdGFyVHJla0NvbW1lbnRzIDwtIG15U3RhclRyZWtEYXRhJGNvbW1lbnRUZXh0W3doaWNoKG15U3RhclRyZWtEYXRhJGNvbW1lbnRUZXh0IT0iTm90X2FwcGxpY2FibGUiKV0Kc3RhclRyZWtDb21tZW50cyA8LSBwYXN0ZShzdGFyVHJla0NvbW1lbnRzICwgY29sbGFwc2UgPSAiICIpCgojIGNvbWJpbmUgdGhlbSB0b2dldGhlciBpbnRvIGEgZGF0YWZyYW1lCmRmX0FMTCA8LSBkYXRhLmZyYW1lKGdyb3VwPWMoIlN0YXJfV2FycyIsIlN0YXJfVHJlayIpLHdvcmRzPWMoc3RhcldhcnNDb21tZW50cyxzdGFyVHJla0NvbW1lbnRzKSkKYGBgCgpEYXRhIHByZS1wcm9jZXNzaW5nOgpgYGB7cn0KIyBzZWFyY2ggZm9yIGFueSB0ZXh0cyB0aGF0IGhhdmUgbm8gY2hhcmFjdGVycyAoaS5lLiBhcmUg4oCZZW1wdHnigJkpCiMgYW5kIHRoZW4gcmVtb3ZlIHRoZXNlIGVsZW1lbnRzIGZyb20gdGhlIHZlY3Rvcgp0b1JlbW92ZSA8LSB3aGljaChkZl9BTEwkd29yZHM9PSIiKQoKIyBhcmUgdGhlcmUgYW55IOKAmWVtcHR54oCZIHRleHQgZWxlbWVudHM/CiMgKGkuZS4gbGVuZ3RoIG9mIHRvUmVtb3ZlIGlzIG5vdCBlcXVhbCB0byB6ZXJvKQojIGlmIHRydWUsIHRoZW4gd2UgcmVtb3ZlIHRoZSBjb3JyZXNwb25kaW5nIHJvd3MgZnJvbSB0aGUgZGF0YWZyYW1lCmlmIChpc1RSVUUobGVuZ3RoKHRvUmVtb3ZlKSE9MCkpIHsKZGZfQUxMIDwtIGRmX0FMTFstdG9SZW1vdmUsXQp9CiMgd2UgY3JlYXRlIGEgY2hhcmFjdGVyIHZlY3RvciBmcm9tIHRoZSAid29yZHMiIGNvbHVtbiBvZiBkZl9BTEwKIyB0aGlzIHdpbGwgYmUgb3VyIGluZGVwZW5kZW50IHZhcmlhYmxlLgojIHdlIGRvIG5vdCB3YW50IHRleHQgYXMgZmFjdG9ycywgc28gd2Ugd2lsbCBjb2VyY2UgaXQgdG8gY2hhcmFjdGVyCndvcmRzIDwtIGRmX0FMTCR3b3JkcwojIHdlIHdpbGwgY29udmVydCB0aGUgY2hhcmFjdGVyIGVuY29kaW5nIHRvIFVURi04CiMganVzdCB0byBiZSBzdXJlIHRoZXJlIGFyZSBubyBvZGQgY2hhcmFjdGVycyB0aGF0CiMgbWF5IGNhdXNlIHByb2JsZW1zIGxhdGVyIG9uCndvcmRzIDwtIGljb252KHdvcmRzLCB0byA9ICJVVEYtOCIpCiMgKiogTUFDIFVTRVJTIE9OTFkgKio6CndvcmRzIDwtIGljb252KHdvcmRzLCB0byA9ICJVVEYtOC1tYWMiKQojIHVzaW5nIOKAmXRt4oCZIHBhY2thZ2Ugd2UgY29udmVydCBjaGFyYWN0ZXIgdmVjdG9yIHRvIGEgVmNvcnB1cyBvYmplY3QgKHZvbGF0aWxlIGNvcnB1cykKY29ycCA8LSBWQ29ycHVzKFZlY3RvclNvdXJjZSh3b3JkcykpCiMjIG5vdyB3ZSBkbyB0cmFuc2Zvcm1hdGlvbnMgb2YgdGV4dCB1c2luZyB0bV9tYXAgKOKAmW1hcHBpbmcgdG8gdGhlIGNvcnB1c+KAmSkKIyBlbGltaW5hdGUgZXh0cmEgd2hpdGVzcGFjZQpjb3JwIDwtIHRtX21hcChjb3JwLCBzdHJpcFdoaXRlc3BhY2UpCiMgY29udmVydCB0byBhbGwgbG93ZXJjYXNlCmNvcnAgPC0gdG1fbWFwKGNvcnAsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCiMgcGVyZm9ybSBzdGVtbWluZyAobm90IGFsd2F5cyB1c2VmdWwhKQojZmJDb3JwdXMgPC0gdG1fbWFwKGZiQ29ycHVzLCBzdGVtRG9jdW1lbnQpCiMgcmVtb3ZlIG51bWJlcnMgKG5vdCBhbHdheXMgdXNlZnVsISkKZmJDb3JwdXMgPC0gdG1fbWFwKGZiQ29ycHVzLCByZW1vdmVOdW1iZXJzKQojIHJlbW92ZSBwdW5jdHVhdGlvbiAobm90IGFsd2F5cyB1c2VmdWwhIGUuZy4gdGV4dCBlbW90aWNvbnMpCmZiQ29ycHVzIDwtIHRtX21hcChmYkNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pCiMgcmVtb3ZlIHN0b3Agd29yZHMgKG5vdCBhbHdheXMgdXNlZnVsISkgLSBkb2luZyB0aGlzIGluIHBlcmwKY29ycCA8LSB0bV9tYXAoY29ycCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKQojIGNyZWF0ZSBhIGRvY3VtZW50LXRlcm0gbWF0cml4CiMgaGFkIHRvIGRvIGl0IHRoaXMgd2F5IHRvIGJlIGFibGUgdG8gdXNlIGNvbG5hbWVzCnRkbSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycCkKdGRtIDwtIGFzLm1hdHJpeCh0ZG0pCiNwcmludCh0ZG0pCmNvbG5hbWVzKHRkbSkgPC0gYygiU3Rhcl9XYXJzIiwiU3Rhcl9UcmVrIikKY29sb3JzeD1jKCJibHVlIiwicmVkIikKYGBgCgpXb3JkIENsb3VkIHZpc3VhbGl6YXRpb246CmBgYHtyfQpyZXF1aXJlKHdvcmRjbG91ZCkKI25vdGU6IGlmIGNoYW5naW5nIHJlcyBvZiBwbmcsIGNhbuKAmXQgaGF2ZSBkaW1lbnNpb25zIGluIHBpeGVscyAobGVkIHRvIHdvcmRjbG91ZHMgd2l0aCB2ZXJ5IGZldyB3b3Jkcy4uLikKIyBwbmcoImZhY2Vib29rX3N0YXJ3YXJzX3N0YXJ0cmVrX2NvbXBhcmlzb25fY2xvdWQucG5nIiwgd2lkdGg9MTIsIGhlaWdodD04LCB1bml0cz0iaW4iLCByZXM9MzAwKQoKI2NvbXBhcmlzb24uY2xvdWQodGRtLG1heC53b3Jkcz0zMDAscmFuZG9tLm9yZGVyPUZBTFNFKQpjb21wYXJpc29uLmNsb3VkKHRkbSxtYXgud29yZHM9MjAwLHJhbmRvbS5vcmRlcj1GQUxTRSxjb2xvcnM9Y29sb3JzeCkKI2NvbW1vbmFsaXR5LmNsb3VkKHRkbSxyYW5kb20ub3JkZXI9RkFMU0UpCgojIGRldi5vZmYoKQpgYGAKCgoKCiMgU29jaWFsIE5ldHdvcmsgVmlzdWFsaXphdGlvbgoKIyMgU21hbGwtc2NhbGUgdmlzdWFsaXphdGlvbgoKIyMjIFlvdXR1YmUgYWN0b3IgbmV0d29yayAodXNlci11c2VyKQpXZSBjYW4gdmlzdWFsaXNlIGEgbmV0d29yayBieSBwbG90dGluZyBpdCBkaXJlY3RseSBpbiBSOgpgYGB7cn0KIyBwbmcoImdfYWN0b3JfeW91dHViZS5wbmciLCB3aWR0aD04MDAsIGhlaWdodD03MDApCnBsb3QoZ19hY3Rvcl95b3V0dWJlLCB2ZXJ0ZXguc2hhcGU9Im5vbmUiLCBlZGdlLndpZHRoPTEuNSwgZWRnZS5jdXJ2ZWQ9LjUsIGVkZ2UuYXJyb3cuc2l6ZT0wLjUsIGFzcD05LzE2LCBtYWluPSJVc2VycyBhcyBhY3RvciBuZXR3b3JrIikKIyBkZXYub2ZmKCkKYGBgCgojIyMgRmFjZWJvb2sgYmltb2RhbCBuZXR3b3JrICh1c2VyLXBvc3QpCgpCZWZvcmUgcGxvdHRpbmcgdGhlIGdyYXBoLCBjaGFuZ2UgdGhlIG5vZGUgY29sb3Igc3VjaCB0aGF0IFBvc3RzIGFyZSByZWQsIHdoaWxlIFVzZXJzIGFyZSB0aGUgZGVmYXVsdCBjb2xvcgooYmx1ZSkuCmBgYHtyfQpWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWspJGNvbG9yIDwtIGlmZWxzZShWKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWspJHR5cGUgPT0gIlBvc3QiLCAicmVkIiwgImJsdWUiKQpgYGAKCldlIGNhbiBzZWUgdGhlIG5ldHdvcmsgd2l0aCB0aGUgZm9sbG93aW5nOgpgYGB7cn0KcGxvdChnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrKQpgYGAKCkluIFJTdHVkaW8sIHRoZSBwbG90IHBhbmUgaXMgZ2VuZXJhbGx5IHRvbyBzbWFsbCBhbmQgc28gYW4gaW1wcm92ZW1lbnQgaXMgdmlhIG9wZW5pbmcgYW4gWDExIGdyYXBoaWNzCmRyaXZlciAob25seSBvbiBtYWNoaW5lcyB3aXRoIGFjY2VzcyB0byBhbiBYIHNlcnZlcik6CmBgYHtyIGV2YWw9RkFMU0V9CngxMSgpCnBsb3QoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlaykKYGBgCgpUaGUgZm9sbG93aW5nIHNldCBvZiBjb21tYW5kcyBwcmludHMgdGhlIHBsb3QgKHdpdGggc29tZSBwbG90IG9wdGlvbnMgdG8gaW1wcm92ZSB0aGUgdmlzdWFsaXNhdGlvbikgdG8gZmlsZToKYGBge3IgZXZhbD1GQUxTRSwgdGlkeT1UUlVFfQojIHBuZygiZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlay5wbmciLCB3aWR0aD04MDAsIGhlaWdodD03MDApCnBsb3QoZ19iaW1vZGFsX2ZhY2Vib29rX3N0YXJfdHJlayx2ZXJ0ZXguc2hhcGU9Im5vbmUiLGVkZ2Uud2lkdGg9MS41LGVkZ2UuY3VydmVkID0gLjUsZWRnZS5hcnJvdy5zaXplPTAuNSx2ZXJ0ZXgubGFiZWwuY29sb3I9VihnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrKSRjb2xvcixhc3A9OS8xNixtYXJnaW49LTAuMTUpCiMgZGV2Lm9mZigpCmBgYAoKIyMjIFR3aXR0ZXIgYWN0b3IgbmV0d29yayAodXNlci11c2VyKQoKTmV4dCwgd2UgY2FuIHZpc3VhbGlzZSB0aGUgbmV0d29yayBieSBwbG90dGluZyBpdCBkaXJlY3RseSBpbiBSOgpgYGB7ciB0aWR5PVRSVUV9CiMgcG5nKCJnX3R3aXR0ZXJfYWN0b3IucG5nIiwgd2lkdGg9ODAwLCBoZWlnaHQ9NzAwKQpwbG90KGdfdHdpdHRlcl9hY3Rvcix2ZXJ0ZXguc2hhcGU9Im5vbmUiLGVkZ2Uud2lkdGg9MS41LGVkZ2UuY3VydmVkID0gLjUsZWRnZS5hcnJvdy5zaXplPTAuNSxhc3A9OS8xNixtYXJnaW49LTAuMTUpCiMgZGV2Lm9mZigpCmBgYAoKCgojIyBMYXJnZS1zY2FsZSB2aXN1YWxpemF0aW9uCgojIyMgSW5zdGFsbGluZyBHZWxwaGkKCllvdSBjYW4gZXhwb3J0IHRoZSBzb2NpYWwgbmV0d29yayBieSBSL1NvY2lhbE1lZGlhTGFiIGFuZCBpbXBvcnQgaXQgaW50byBHZXBoaSwKd2hpY2ggaXMgYSBmYW50YXN0aWMgbmV0d29yayB2aXN1YWxpc2F0aW9uIHByb2dyYW0uIElmIHlvdSB3aXNoIHRvIGRvIHRoaXMsIHRoZW4geW91IG5lZWQgdG8gZG93bmxvYWQgYW5kCmluc3RhbGwgW0dlcGhpXShodHRwczovL2dlcGhpLm9yZy91c2Vycy9kb3dubG9hZC8pLgoKVG8gZG8gbmV0d29yayBhbmFseXNpcyAoZS5nLiBleHBvcnQgbmV0d29yayB0byBmaWxlKSB3ZSBhbHNvIG5lZWQgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwgdGhlIGlncmFwaCBwYWNrYWdlIGNyZWF0ZWQgYnkgR2Fib3IgQ3NhcmRpLgpXZSBjYW4gZG8gdGhpcyBieSBlbnRlcmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW50byB0aGUgUiBjb25zb2xlOgoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiaWdyYXBoIikgIyBJbnN0YWxsIHBhY2thZ2UKYGBgCgpPciBsb2FkIHRoZSBwYWNrYWdlIGlmIGl0IGlzIGFscmVhZHkgaW5zdGFsbGVkOgpgYGB7cn0KcmVxdWlyZShpZ3JhcGgpICNMb2FkIHBhY2thZ2UKYGBgCgoKIyMjIFVzaW5nIEdlbHBoaQoKQmVjYXVzZSBvZiB0aGUgc2l6ZSBvZiB0aGUgdHdvIG5ldHdvcmtzIHdlIGhhdmUgY3JlYXRlZCwgd2Ugd29uJ3QgdHJ5IHRvIHZpc3VhbGlzZSB0aGVtIGluIFIuIFRvIHZpc3VhbGlzZSB0aGUgIlN0YXIgVHJlayIiIG5ldHdvcmtpbmcgdXNpbmcgR2VwaGksIGZpcnN0IGV4cG9ydCB0aGUgbmV0d29yayB1c2lnIHRoZSAnZ3JhcGhtbCcgbmV0d29yayBmaWxlIGZvcm1hdDoKYGBge3J9CndyaXRlLmdyYXBoKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWssICJnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrLmdyYXBobWwiLCBmb3JtYXQ9ImdyYXBobWwiKQpgYGAKCkdlcGhpIGlzIHRoZSBpZGVhbCBzb2Z0d2FyZSBmb3IgdmlzdWFsaXNpbmcgbmV0d29ya3MgY3JlYXRlZCBpbiBTb2NpYWxNZWRpYUxhYiAuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGltcG9ydAppbnRvIEdlcGhpIHRoZSBJbnN0YWdyYW0gbmV0d29yayB3ZSBnZW5lcmF0ZWQgZWFybGllciwgaW4gb3JkZXIgdG8gdmlzdWFsaXNlIGl0LgoKRmlyc3QsIG9wZW4gR2VwaGkuIFlvdSBzaG91bGQgYmUgcHJlc2VudGVkIHdpdGggYSBkaWFsb2d1ZSBib3guIENsaWNrIOKAnE9wZW4gR3JhcGggRmlsZS4uLuKAnSAob3Igc2VsZWN0IGZyb20KbWVudSDigJhGaWxl4oCZ4oCTPuKAmE9wZW7igJkpIGFuZCBuYXZpZ2F0ZSB0byB0aGUgd29ya2luZyBkaXJlY3Rvcnkgb2YgeW91ciBSIHByb2plY3QgZm9yIHRoaXMgdHV0b3JpYWwuIFRoZSB3b3JraW5nCmRpcmVjdG9yeSBzaG91bGQgY29udGFpbiBhIGdyYXBobWwgZmlsZSB0aGF0IHdhcyBnZW5lcmF0ZWQgaW4gdGhlIEluc3RhZ3JhbSBzZWN0aW9uLiBTZWxlY3QgdGhpcyBmaWxlIGFuZCBjbGljawpvcGVuLgoKWW91IHdpbGwgYmUgcHJlc2VudGVkIHdpdGggYW4g4oCYSW1wb3J0IHJlcG9ydOKAmSBkaWFsb2d1ZSBib3ggcHJvdmlkaW5nIGluZm9ybWF0aW9uIGFib3V0IHRoZSBuZXR3b3JrLiBTaW1wbHkKY2xpY2sgT0suIFlvdXIgbmV0d29yayB3aWxsIHRoZW4gYmUgcHJlc2VudGVkIGluIGEgdmVyeSBhd2Z1bCBmb3JtYXQgLSBwb3NzaWJseSBsb29raW5nIGxpa2UgYSBncmF5LWNvbG91cmVkCmhhaXJiYWxsLiBMZXTigJlzIGZpeCB0aGF0LgoKIVtdKGdfYmltb2RhbF9mYWNlYm9va19zdGFyX3RyZWsuZ2VscGhpLnBuZykKCkluIHRoZSB0b3AtbGVmdCBoYW5kIGJveCB1bmRlciB0aGUg4oCYQXBwZWFyYW5jZeKAmSB0YWIsIGNsaWNrIHRoZSBsaXR0bGUgaWNvbiB0aGF0IHNheXMg4oCYU2l6ZeKAmSB3aGVuIHlvdSBob3ZlciBvdmVyCml0LiBTZWxlY3Qg4oCcSW5kZWdyZWXigJ0gZnJvbSB0aGUgZHJvcGRvd24gbWVudS4gU2V0IHRoZSBtaW4gc2l6ZSB0byA1IGFuZCB0aGUgbWF4IHNpemUgdG8gNTAsIGFuZCBjbGljayBBcHBseS4KTm93IHRoZSBub2RlcyBpbiB5b3VyIG5ldHdvcmsgYXJlIHNpemVkIGJhc2VkIG9uIGhvdyBtYW55IGlubGlua3MgdGhleSByZWNlaXZlIGZyb20gb3RoZXIgbm9kZXMuCkl0J3Mgc3RpbGwgYW4gdWdseSBuZXR3b3JrIHRob3VnaCEKCiFbXShnX2JpbW9kYWxfZmFjZWJvb2tfc3Rhcl90cmVrLmdlbHBoaTIucG5nKQoK