• Load abstracts from Google Scholar
  • Transform and clean data
  • Create wordcloud
  • Limitations
  • Wrapping everything in a function

This guide will explain how to visualize key themes from a Google Scholar profile using a WordCloud. This tutorial is designed to be accessible to users with only a basic understanding of R programming language.

(The entire code for this tutorial is consolidated into a single function and presented at the end of this document. If you’re only interested in generating the wordcloud, and don’t care about the procedure, feel free to skip to the end of this document.”)

Load abstracts from Google Scholar

First, we need to specify a Google Scholar profile using a researcher’s name. To do this, assign an author’s first and last name to the objects “FirstName” and “LastName” (respectfully). For the purposes of this tutorial, I’ll use my own name.

FirstName<-"Zak"  
LastName<-"Witkower"

Next, retrieve all items indexed by Google Scholar (e.g., publications) for the specified researcher, using the scholar library.

library(scholar)
library(tibble) #for data cleaning

# Retrieve unique Google Scholar identifier using first and last name objects created above. 
IDnumber<-get_scholar_id(first_name = FirstName, 
                         last_name = LastName)

# Generate list of articles indexed in Google Scholar
Mypublications<-tibble(get_publications(IDnumber))

The resulting dataframe is in wide form, with each row representing a unique item indexed by Google Scholar for the specified researcher, and each column representing a different variable or attribute (e.g., title, journal name, number of citations, etc).

head(Mypublications, 10)
## # A tibble: 10 × 8
##    title                           author journal number cites  year cid   pubid
##    <chr>                           <chr>  <chr>   <chr>  <dbl> <dbl> <chr> <chr>
##  1 Two signals of social rank: Pr… Z Wit… Journa… 118 (…   148  2020 1283… qjMa…
##  2 Bodily communication of emotio… Z Wit… Emotio… 11 (2…   125  2019 1582… UeHW…
##  3 Predicting cyberbullying perpe… C Bar… Aggres… 43 (2…   102  2017 5427… 2osO…
##  4 The evolution of pride and soc… JL Tr… Advanc… 62, 5…    85  2020 7726… hqOj…
##  5 A facial-action imposter: How … Z Wit… Psycho… 30 (6…    47  2019 3325… W7OE…
##  6 The psychological structure, s… E Mer… Curren… 39, 1…    39  2021 8498… ULOm…
##  7 How affect shapes status: Dist… Z Wit… Curren… 33, 1…    37  2020 1574… YsMS…
##  8 Breaking the link between prov… C Bar… Aggres… 42 (6…    19  2016 5389… 9yKS…
##  9 Evidence for distinct facial s… JD Ma… Affect… 2, 14…    18  2021 1827… KlAt…
## 10 Beyond face value: Evidence fo… Z Wit… Affect… 2 (3)…    17  2021 1677… _kc_…

Next we will to retrieve the abstracts for each publication using a loop, and append each abstract to our dataframe.

# initialize new column to be filled with abstracts
Mypublications$abstract<-rep("initialize", nrow(Mypublications))

#"for" loop to add abstract for each publication
for (i in 1:nrow(Mypublications)) {
  Abstracts <- get_publication_abstract(id = IDnumber, 
                                     pub_id = Mypublications$pubid[i])
  
#This is included to mitigate  "replacement has length zero" error, in case there is an issue finding a file 
  ifelse(length(Abstracts) == 0, NA, Mypublications$abstract[i] <- Abstracts) 
}

Transform and clean data

Now we are ready to turn our dataframe into a corpus, which we will later use to create a Term Document Matrix (TDM). A TDM is a numerical representation of the text data that will allow us to perform quantitative analysis and create a wordcloud.

library(tm)
library(tidytext)
library(tidyverse)

#create corpus from abstracts
MyAbstracts.corpus<-VCorpus(VectorSource(Mypublications$abstract))

#create Term Document Matrix (TDM) from corpus
MyAbstracts.TDM<-TermDocumentMatrix(MyAbstracts.corpus, 
                                    control =  #Data editing:
                                      list(removePunctuation = TRUE, 
                                           stopwords = TRUE, 
                                           tolower = TRUE,
                                           stemming = F, 
                                           removeNumbers = TRUE))

Lets identify the 100 most frequently used terms. I do this by summing the frequency of each unique term across all documents.

tidy.TDM<-tidy(MyAbstracts.TDM) %>% 
  group_by(term) %>% 
  summarise(count,count = sum(count)) %>% #sum frequency
  unique()%>% 
  ungroup() %>%
  arrange(desc(count)) #rearrange based on frequency of term

#only retain the top 100 most frequently used terms
tidy.TDM<-head(tidy.TDM, 100) 

head(tidy.TDM)

Create wordcloud

Finally, lets create a wordcloud.

(Note: You can adjust the aesthetics of the wordcloud using the code below.)

library(wordcloud2)
library(randomcoloR) 

#create wordcloud
wordcloud2(tidy.TDM, 
           color = randomColor(nrow(tidy.TDM), #create color palette with randomcoloR
                               hue = "random", 
                               luminosity = "dark"),
           fontWeight = "bold",  #bold all items
           rotate = 0,           #max rotation of words within wordcloud
           size=.75,             #size of wordcloud
           fontFamily = "Times") #font of wordcloud   

Limitations

If the specified author includes their middle initial in their Google Scholar profile, or has three names, you may encounter problems generating their wordcloud. There are a few possible solutions:
* Input their first and last name exclusively (e.g., input “Jessica L. Tracy” as “Jessica Tracy”, or “Gerben van Kleef” as “Gerben Kleef”)
* Input their first and middle initials as their first name (e.g., input “Friedrich M. Götz” as “FM Götz”)
* If all else fails, you can retrieve a researcher’s Google Scholar ID manually from the hyperlink of their Google Scholar page, instead of retrieving it based on their first and last name (i.e., the method used here). Look for text “user=” embedded within the hyperlink, and extract the 12 letters that follow. You can assign their ID as a character string to the object “IDnumber”, and skip the first step of retrieving Google Scholar IDs with the “get_scholar_id” function

This method relies heavily on querying Google Scholar, which may introduce some complications. For example:
* An internet connection is required
* The specified researcher must have an active Google Scholar profile
* The method will extract all files indexed by Google Scholar for the specified profile, which may include non-peer-reviewed files or supplemental materials
* Rate limits imposed by Google Scholar may restrict the total number and speed of your requests. * If you try to generate a wordcloud for a particularly productive researcher – which requires you to request all of the publications and abstracts of a specified author – you may exceed Google’s rate limit (i.e., too many requests in a short period of time), resulting in an error.

You may wish to alternatively create a wordcloud that visualizes your research by uploading PDF files of your research saved on your local drive. This approach, which is described in more detail on my website.

If you have any questions, comments, or concerns, please get in touch with me. I’m happy to help! Email: Zakwitkower@gmail.com
Website: www.ZakWitkower.com
LinkedIn: Zak Witkower
Twitter: @Zakwitkower

Wrapping everything in a function

For simplicity, I’ve consolidated all the steps into a single function that automates the creation of a wordcloud using just your first and last name. Enter the first and last name of the researcher whose research you want to visualize at the top of the code chunk (assigned to objects “FirstName” and “LastName”), and then run the rest of the code. The function will handle all additional steps, and output a wordcloud.

#####################################################################
####### Please input the first and last name of a researcher ########
#####################################################################

FirstName<-"Zak"  
LastName<-"Witkower"

########################################################################################
#### After assigning a name above, run the rest of the code to create the function: ####
########################################################################################

#load libraries
require(scholar)
require(tibble) 
require(tm)
require(tidytext)
require(tidyverse)
require(wordcloud2)
require(randomcoloR) 

#Create function
GoogleScholarWordCloud<-function(first,last){
  
  #Get ID number from name
  IDnumber<-get_scholar_id(first_name = first, 
                           last_name = last)
  
  #Get publications
  Mypublications<-tibble(get_publications(IDnumber))
  
  #Add abstracts
  Mypublications$abstract<-rep("initialize", nrow(Mypublications))
  for (i in 1:nrow(Mypublications)) {
    Abstracts <- get_publication_abstract(id = IDnumber, 
                                          pub_id = Mypublications$pubid[i])
    ifelse(length(Abstracts) == 0, NA, Mypublications$abstract[i] <- Abstracts) 
  }
  
  #Create Corpus
  MyAbstracts.corpus<-VCorpus(VectorSource(Mypublications$abstract))
  
  #Create TDM from corpus
  MyAbstracts.TDM<-TermDocumentMatrix(MyAbstracts.corpus,
                                      control = 
                                        list(removePunctuation = TRUE,  
                                             stopwords = TRUE,
                                             tolower = TRUE,
                                             stemming = F,
                                             removeNumbers = TRUE))
  #Tidy TDM
  MyAbstracts.TDM<-tidy(MyAbstracts.TDM) %>% 
    group_by(term) %>% 
    summarise(count,count = sum(count)) %>% 
    unique()%>%  
    ungroup() %>%
    arrange(desc(count))    
  
  #retain top 100 most frequently used words
  MyAbstracts.TDM<-head(MyAbstracts.TDM, 100) 
  
  #create wordcloud
  wordcloud<-wordcloud2(MyAbstracts.TDM,
             fontWeight = "bold",  #bold all items
             rotate = 0,           #max rotation of words 
             size=.75,             #size of wordcloud
             fontFamily = "Times", #font of wordcloud   
             color = randomColor(nrow(MyAbstracts.TDM), #create color palette
                                 hue = "random", 
                                 luminosity = "dark")) 
  
  return(wordcloud)
  }

##################################################################
### Run the function we just created, using the assigned name ###
##################################################################

GoogleScholarWordCloud(FirstName, LastName)

############################ End. ###################################
LS0tCnRpdGxlOiAiVmlzdWFsaXplIHlvdXIgR29vZ2xlIFNjaG9sYXIgcHJvZmlsZSB1c2luZyBhIHdvcmRjbG91ZCIKYXV0aG9yOiAnW1phayBXaXRrb3dlcl0oaHR0cHM6Ly93d3cuemFrd2l0a293ZXIuY29tLyknCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCjxkaXYgY2xhc3M9InRvY2lmeS1leHRlbmQtcGFnZSIgZGF0YS11bmlxdWU9InRvY2lmeS1leHRlbmQtcGFnZSIgc3R5bGU9ImhlaWdodDogMDsiPjwvZGl2PiAKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgpUaGlzIGd1aWRlIHdpbGwgZXhwbGFpbiBob3cgdG8gdmlzdWFsaXplIGtleSB0aGVtZXMgZnJvbSBhIEdvb2dsZSBTY2hvbGFyIHByb2ZpbGUgdXNpbmcgYSBXb3JkQ2xvdWQuIFRoaXMgdHV0b3JpYWwgaXMgZGVzaWduZWQgdG8gYmUgYWNjZXNzaWJsZSB0byB1c2VycyB3aXRoIG9ubHkgYSBiYXNpYyB1bmRlcnN0YW5kaW5nIG9mIFIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UuCgooKlRoZSBlbnRpcmUgY29kZSBmb3IgdGhpcyB0dXRvcmlhbCBpcyBjb25zb2xpZGF0ZWQgaW50byBhIHNpbmdsZSBmdW5jdGlvbiBhbmQgcHJlc2VudGVkIGF0IHRoZSBlbmQgb2YgdGhpcyBkb2N1bWVudC4gSWYgeW91J3JlIG9ubHkgaW50ZXJlc3RlZCBpbiBnZW5lcmF0aW5nIHRoZSB3b3JkY2xvdWQsIGFuZCBkb24ndCBjYXJlIGFib3V0IHRoZSBwcm9jZWR1cmUsIGZlZWwgZnJlZSB0byBza2lwIHRvIHRoZSBlbmQgb2YgdGhpcyBkb2N1bWVudC4iKikKCiMgTG9hZCBhYnN0cmFjdHMgZnJvbSBHb29nbGUgU2Nob2xhcgoKRmlyc3QsIHdlIG5lZWQgdG8gc3BlY2lmeSBhIEdvb2dsZSBTY2hvbGFyIHByb2ZpbGUgdXNpbmcgYSByZXNlYXJjaGVyJ3MgbmFtZS4gVG8gZG8gdGhpcywgYXNzaWduIGFuIGF1dGhvcidzIGZpcnN0IGFuZCBsYXN0IG5hbWUgdG8gdGhlIG9iamVjdHMgIkZpcnN0TmFtZSIgYW5kICJMYXN0TmFtZSIgKHJlc3BlY3RmdWxseSkuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyB0dXRvcmlhbCwgSSdsbCB1c2UgbXkgb3duIG5hbWUuCgpgYGB7ciwgaW5jbHVkZT1UUlVFLHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpGaXJzdE5hbWU8LSJaYWsiICAKTGFzdE5hbWU8LSJXaXRrb3dlciIKYGBgCgpOZXh0LCByZXRyaWV2ZSBhbGwgaXRlbXMgaW5kZXhlZCBieSBHb29nbGUgU2Nob2xhciAoZS5nLiwgcHVibGljYXRpb25zKSBmb3IgdGhlIHNwZWNpZmllZCByZXNlYXJjaGVyLCB1c2luZyB0aGUgWypzY2hvbGFyKl0oaHR0cHM6Ly9naXRodWIuY29tL2prZWlyc3RlYWQvc2Nob2xhcikgbGlicmFyeS4gCgpgYGB7ciwgaW5jbHVkZT1UUlVFLCByZXN1bHRzID0gInNob3ciLCB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgcGFnZWQucHJpbnQ9RkFMU0V9CmxpYnJhcnkoc2Nob2xhcikKbGlicmFyeSh0aWJibGUpICNmb3IgZGF0YSBjbGVhbmluZwoKIyBSZXRyaWV2ZSB1bmlxdWUgR29vZ2xlIFNjaG9sYXIgaWRlbnRpZmllciB1c2luZyBmaXJzdCBhbmQgbGFzdCBuYW1lIG9iamVjdHMgY3JlYXRlZCBhYm92ZS4gCklEbnVtYmVyPC1nZXRfc2Nob2xhcl9pZChmaXJzdF9uYW1lID0gRmlyc3ROYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RfbmFtZSA9IExhc3ROYW1lKQoKIyBHZW5lcmF0ZSBsaXN0IG9mIGFydGljbGVzIGluZGV4ZWQgaW4gR29vZ2xlIFNjaG9sYXIKTXlwdWJsaWNhdGlvbnM8LXRpYmJsZShnZXRfcHVibGljYXRpb25zKElEbnVtYmVyKSkKYGBgCgpUaGUgcmVzdWx0aW5nIGRhdGFmcmFtZSBpcyBpbiAqd2lkZSogZm9ybSwgd2l0aCBlYWNoIHJvdyByZXByZXNlbnRpbmcgYSB1bmlxdWUgaXRlbSBpbmRleGVkIGJ5IEdvb2dsZSBTY2hvbGFyIGZvciB0aGUgc3BlY2lmaWVkIHJlc2VhcmNoZXIsIGFuZCBlYWNoIGNvbHVtbiByZXByZXNlbnRpbmcgYSBkaWZmZXJlbnQgdmFyaWFibGUgb3IgYXR0cmlidXRlIChlLmcuLCB0aXRsZSwgam91cm5hbCBuYW1lLCBudW1iZXIgb2YgY2l0YXRpb25zLCBldGMpLiAKCmBgYHtyLCBpbmNsdWRlPVRSVUUsIHJlc3VsdHMgPSAic2hvdyIsIHdhcm5pbmc9RiwgbWVzc2FnZT1GLCBwYWdlZC5wcmludD1GQUxTRX0KaGVhZChNeXB1YmxpY2F0aW9ucywgMTApCmBgYAoKTmV4dCB3ZSB3aWxsIHRvIHJldHJpZXZlIHRoZSBhYnN0cmFjdHMgZm9yIGVhY2ggcHVibGljYXRpb24gdXNpbmcgYSBsb29wLCBhbmQgYXBwZW5kIGVhY2ggYWJzdHJhY3QgdG8gb3VyIGRhdGFmcmFtZS4KCmBgYHtyLCBpbmNsdWRlPVRSVUUsIHJlc3VsdHMgPSAiaGlkZSIsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQoKIyBpbml0aWFsaXplIG5ldyBjb2x1bW4gdG8gYmUgZmlsbGVkIHdpdGggYWJzdHJhY3RzCk15cHVibGljYXRpb25zJGFic3RyYWN0PC1yZXAoImluaXRpYWxpemUiLCBucm93KE15cHVibGljYXRpb25zKSkKCiMiZm9yIiBsb29wIHRvIGFkZCBhYnN0cmFjdCBmb3IgZWFjaCBwdWJsaWNhdGlvbgpmb3IgKGkgaW4gMTpucm93KE15cHVibGljYXRpb25zKSkgewogIEFic3RyYWN0cyA8LSBnZXRfcHVibGljYXRpb25fYWJzdHJhY3QoaWQgPSBJRG51bWJlciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWJfaWQgPSBNeXB1YmxpY2F0aW9ucyRwdWJpZFtpXSkKICAKI1RoaXMgaXMgaW5jbHVkZWQgdG8gbWl0aWdhdGUgICJyZXBsYWNlbWVudCBoYXMgbGVuZ3RoIHplcm8iIGVycm9yLCBpbiBjYXNlIHRoZXJlIGlzIGFuIGlzc3VlIGZpbmRpbmcgYSBmaWxlIAogIGlmZWxzZShsZW5ndGgoQWJzdHJhY3RzKSA9PSAwLCBOQSwgTXlwdWJsaWNhdGlvbnMkYWJzdHJhY3RbaV0gPC0gQWJzdHJhY3RzKSAKfQpgYGAKCiMgVHJhbnNmb3JtIGFuZCBjbGVhbiBkYXRhCk5vdyB3ZSBhcmUgcmVhZHkgdG8gdHVybiBvdXIgZGF0YWZyYW1lIGludG8gYSBjb3JwdXMsIHdoaWNoIHdlIHdpbGwgbGF0ZXIgdXNlIHRvIGNyZWF0ZSBhIFRlcm0gRG9jdW1lbnQgTWF0cml4IChURE0pLiBBIFRETSBpcyBhIG51bWVyaWNhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgdGV4dCBkYXRhIHRoYXQgd2lsbCBhbGxvdyB1cyB0byBwZXJmb3JtIHF1YW50aXRhdGl2ZSBhbmFseXNpcyBhbmQgY3JlYXRlIGEgd29yZGNsb3VkLgoKYGBge3IsIGluY2x1ZGU9VFJVRSwgcmVzdWx0cyA9ICJoaWRlIiwgd2FybmluZz1GLCBtZXNzYWdlPUZ9CmxpYnJhcnkodG0pCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkodGlkeXZlcnNlKQoKI2NyZWF0ZSBjb3JwdXMgZnJvbSBhYnN0cmFjdHMKTXlBYnN0cmFjdHMuY29ycHVzPC1WQ29ycHVzKFZlY3RvclNvdXJjZShNeXB1YmxpY2F0aW9ucyRhYnN0cmFjdCkpCgojY3JlYXRlIFRlcm0gRG9jdW1lbnQgTWF0cml4IChURE0pIGZyb20gY29ycHVzCk15QWJzdHJhY3RzLlRETTwtVGVybURvY3VtZW50TWF0cml4KE15QWJzdHJhY3RzLmNvcnB1cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSAgI0RhdGEgZWRpdGluZzoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHJlbW92ZVB1bmN0dWF0aW9uID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wd29yZHMgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvbG93ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlbW1pbmcgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZU51bWJlcnMgPSBUUlVFKSkKCmBgYAoKTGV0cyBpZGVudGlmeSB0aGUgMTAwIG1vc3QgZnJlcXVlbnRseSB1c2VkIHRlcm1zLiBJIGRvIHRoaXMgYnkgc3VtbWluZyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdW5pcXVlIHRlcm0gYWNyb3NzIGFsbCBkb2N1bWVudHMuCgpgYGB7ciwgaW5jbHVkZT1UUlVFLCByZXN1bHRzID0gImhpZGUiLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KdGlkeS5URE08LXRpZHkoTXlBYnN0cmFjdHMuVERNKSAlPiUgCiAgZ3JvdXBfYnkodGVybSkgJT4lIAogIHN1bW1hcmlzZShjb3VudCxjb3VudCA9IHN1bShjb3VudCkpICU+JSAjc3VtIGZyZXF1ZW5jeQogIHVuaXF1ZSgpJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnQpKSAjcmVhcnJhbmdlIGJhc2VkIG9uIGZyZXF1ZW5jeSBvZiB0ZXJtCgojb25seSByZXRhaW4gdGhlIHRvcCAxMDAgbW9zdCBmcmVxdWVudGx5IHVzZWQgdGVybXMKdGlkeS5URE08LWhlYWQodGlkeS5URE0sIDEwMCkgCgpoZWFkKHRpZHkuVERNKQpgYGAKCiMgQ3JlYXRlIHdvcmRjbG91ZApGaW5hbGx5LCBsZXRzIGNyZWF0ZSBhIHdvcmRjbG91ZC4KCigqTm90ZTogWW91IGNhbiBhZGp1c3QgdGhlIGFlc3RoZXRpY3Mgb2YgdGhlIHdvcmRjbG91ZCB1c2luZyB0aGUgY29kZSBiZWxvdy4qKQoKYGBge3IsIGluY2x1ZGU9VFJVRSwgd2FybmluZz1GLCBtZXNzYWdlPUZ9CmxpYnJhcnkod29yZGNsb3VkMikKbGlicmFyeShyYW5kb21jb2xvUikgCgojY3JlYXRlIHdvcmRjbG91ZAp3b3JkY2xvdWQyKHRpZHkuVERNLCAKICAgICAgICAgICBjb2xvciA9IHJhbmRvbUNvbG9yKG5yb3codGlkeS5URE0pLCAjY3JlYXRlIGNvbG9yIHBhbGV0dGUgd2l0aCByYW5kb21jb2xvUgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHVlID0gInJhbmRvbSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbHVtaW5vc2l0eSA9ICJkYXJrIiksCiAgICAgICAgICAgZm9udFdlaWdodCA9ICJib2xkIiwgICNib2xkIGFsbCBpdGVtcwogICAgICAgICAgIHJvdGF0ZSA9IDAsICAgICAgICAgICAjbWF4IHJvdGF0aW9uIG9mIHdvcmRzIHdpdGhpbiB3b3JkY2xvdWQKICAgICAgICAgICBzaXplPS43NSwgICAgICAgICAgICAgI3NpemUgb2Ygd29yZGNsb3VkCiAgICAgICAgICAgZm9udEZhbWlseSA9ICJUaW1lcyIpICNmb250IG9mIHdvcmRjbG91ZCAgIApgYGAKCiMgTGltaXRhdGlvbnMKSWYgdGhlIHNwZWNpZmllZCBhdXRob3IgaW5jbHVkZXMgdGhlaXIgbWlkZGxlIGluaXRpYWwgaW4gdGhlaXIgR29vZ2xlIFNjaG9sYXIgcHJvZmlsZSwgb3IgaGFzIHRocmVlIG5hbWVzLCB5b3UgbWF5IGVuY291bnRlciBwcm9ibGVtcyBnZW5lcmF0aW5nIHRoZWlyIHdvcmRjbG91ZC4gVGhlcmUgYXJlIGEgZmV3IHBvc3NpYmxlIHNvbHV0aW9uczogIAoqIElucHV0IHRoZWlyIGZpcnN0IGFuZCBsYXN0IG5hbWUgZXhjbHVzaXZlbHkgKGUuZy4sIGlucHV0ICJKZXNzaWNhIEwuIFRyYWN5IiBhcyAiSmVzc2ljYSBUcmFjeSIsIG9yICAiR2VyYmVuIHZhbiBLbGVlZiIgYXMgIkdlcmJlbiBLbGVlZiIpICAKKiBJbnB1dCB0aGVpciBmaXJzdCBhbmQgbWlkZGxlIGluaXRpYWxzIGFzIHRoZWlyIGZpcnN0IG5hbWUgKGUuZy4sIGlucHV0ICJGcmllZHJpY2ggTS4gR8O2dHoiIGFzICJGTSBHw7Z0eiIpICAKKiBJZiBhbGwgZWxzZSBmYWlscywgeW91IGNhbiByZXRyaWV2ZSBhIHJlc2VhcmNoZXIncyBHb29nbGUgU2Nob2xhciBJRCBtYW51YWxseSBmcm9tIHRoZSBoeXBlcmxpbmsgb2YgdGhlaXIgR29vZ2xlIFNjaG9sYXIgcGFnZSwgaW5zdGVhZCBvZiByZXRyaWV2aW5nIGl0IGJhc2VkIG9uIHRoZWlyIGZpcnN0IGFuZCBsYXN0IG5hbWUgKGkuZS4sIHRoZSBtZXRob2QgdXNlZCBoZXJlKS4gTG9vayBmb3IgdGV4dCAidXNlcj0iIGVtYmVkZGVkIHdpdGhpbiB0aGUgaHlwZXJsaW5rLCBhbmQgZXh0cmFjdCB0aGUgMTIgbGV0dGVycyB0aGF0IGZvbGxvdy4gWW91IGNhbiBhc3NpZ24gdGhlaXIgSUQgYXMgYSBjaGFyYWN0ZXIgc3RyaW5nIHRvIHRoZSBvYmplY3QgIklEbnVtYmVyIiwgYW5kIHNraXAgdGhlIGZpcnN0IHN0ZXAgb2YgcmV0cmlldmluZyBHb29nbGUgU2Nob2xhciBJRHMgd2l0aCB0aGUgImdldF9zY2hvbGFyX2lkIiBmdW5jdGlvbiAgCgpUaGlzIG1ldGhvZCByZWxpZXMgaGVhdmlseSBvbiBxdWVyeWluZyBHb29nbGUgU2Nob2xhciwgd2hpY2ggbWF5IGludHJvZHVjZSBzb21lIGNvbXBsaWNhdGlvbnMuIEZvciBleGFtcGxlOiAgCiogQW4gaW50ZXJuZXQgY29ubmVjdGlvbiBpcyByZXF1aXJlZCAgCiogVGhlIHNwZWNpZmllZCByZXNlYXJjaGVyIG11c3QgaGF2ZSBhbiBhY3RpdmUgR29vZ2xlIFNjaG9sYXIgcHJvZmlsZSAgIAoqIFRoZSBtZXRob2Qgd2lsbCBleHRyYWN0IGFsbCBmaWxlcyBpbmRleGVkIGJ5IEdvb2dsZSBTY2hvbGFyIGZvciB0aGUgc3BlY2lmaWVkIHByb2ZpbGUsIHdoaWNoIG1heSBpbmNsdWRlIG5vbi1wZWVyLXJldmlld2VkIGZpbGVzIG9yIHN1cHBsZW1lbnRhbCBtYXRlcmlhbHMgICAKKiBSYXRlIGxpbWl0cyBpbXBvc2VkIGJ5IEdvb2dsZSBTY2hvbGFyIG1heSByZXN0cmljdCB0aGUgdG90YWwgbnVtYmVyIGFuZCBzcGVlZCBvZiB5b3VyIHJlcXVlc3RzLgoqIElmIHlvdSB0cnkgdG8gZ2VuZXJhdGUgYSB3b3JkY2xvdWQgZm9yIGEgcGFydGljdWxhcmx5IHByb2R1Y3RpdmUgcmVzZWFyY2hlciAtLSB3aGljaCByZXF1aXJlcyB5b3UgdG8gcmVxdWVzdCBhbGwgb2YgdGhlIHB1YmxpY2F0aW9ucyBhbmQgYWJzdHJhY3RzIG9mIGEgc3BlY2lmaWVkIGF1dGhvciAtLSB5b3UgbWF5IGV4Y2VlZCBHb29nbGUncyByYXRlIGxpbWl0IChpLmUuLCB0b28gbWFueSByZXF1ZXN0cyBpbiBhIHNob3J0IHBlcmlvZCBvZiB0aW1lKSwgcmVzdWx0aW5nIGluIGFuIGVycm9yLiAKCllvdSBtYXkgd2lzaCB0byBhbHRlcm5hdGl2ZWx5IGNyZWF0ZSBhIHdvcmRjbG91ZCB0aGF0IHZpc3VhbGl6ZXMgeW91ciByZXNlYXJjaCBieSB1cGxvYWRpbmcgUERGIGZpbGVzIG9mIHlvdXIgcmVzZWFyY2ggc2F2ZWQgb24geW91ciBsb2NhbCBkcml2ZS4gVGhpcyBhcHByb2FjaCwgd2hpY2ggaXMgZGVzY3JpYmVkIGluIG1vcmUgZGV0YWlsIFsqb24gbXkgd2Vic2l0ZSpdKGh0dHBzOi8vd3d3Lnpha3dpdGtvd2VyLmNvbS8pLgoKSWYgeW91IGhhdmUgYW55IHF1ZXN0aW9ucywgY29tbWVudHMsIG9yIGNvbmNlcm5zLCBwbGVhc2UgZ2V0IGluIHRvdWNoIHdpdGggbWUuIEknbSBoYXBweSB0byBoZWxwIQpFbWFpbDogW1pha3dpdGtvd2VyQGdtYWlsLmNvbV0obWFpbHRvOlpha3dpdGtvd2VyQGdtYWlsLmNvbSkgIApXZWJzaXRlOiBbd3d3Llpha1dpdGtvd2VyLmNvbV0od3d3Llpha1dpdGtvd2VyLmNvbSkgIApMaW5rZWRJbjogW1phayBXaXRrb3dlcl0oaHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL3pha3dpdGtvd2VyLykgIApUd2l0dGVyOiBbQFpha3dpdGtvd2VyXShodHRwczovL3R3aXR0ZXIuY29tL3pha3dpdGtvd2VyKSAgCgojIFdyYXBwaW5nIGV2ZXJ5dGhpbmcgaW4gYSBmdW5jdGlvbgpGb3Igc2ltcGxpY2l0eSwgSSd2ZSBjb25zb2xpZGF0ZWQgYWxsIHRoZSBzdGVwcyBpbnRvIGEgc2luZ2xlIGZ1bmN0aW9uIHRoYXQgYXV0b21hdGVzIHRoZSBjcmVhdGlvbiBvZiBhIHdvcmRjbG91ZCB1c2luZyBqdXN0IHlvdXIgZmlyc3QgYW5kIGxhc3QgbmFtZS4gRW50ZXIgdGhlIGZpcnN0IGFuZCBsYXN0IG5hbWUgb2YgdGhlIHJlc2VhcmNoZXIgd2hvc2UgcmVzZWFyY2ggeW91IHdhbnQgdG8gdmlzdWFsaXplIGF0IHRoZSB0b3Agb2YgdGhlIGNvZGUgY2h1bmsgKGFzc2lnbmVkIHRvIG9iamVjdHMgIkZpcnN0TmFtZSIgYW5kICJMYXN0TmFtZSIpLCBhbmQgdGhlbiBydW4gdGhlIHJlc3Qgb2YgdGhlIGNvZGUuIFRoZSBmdW5jdGlvbiB3aWxsIGhhbmRsZSBhbGwgYWRkaXRpb25hbCBzdGVwcywgYW5kIG91dHB1dCBhIHdvcmRjbG91ZC4KCmBgYHtyLCBpbmNsdWRlPVQsIHJlc3VsdHMgPSAiaGlkZSIsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyBQbGVhc2UgaW5wdXQgdGhlIGZpcnN0IGFuZCBsYXN0IG5hbWUgb2YgYSByZXNlYXJjaGVyICMjIyMjIyMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKRmlyc3ROYW1lPC0iWmFrIiAgCkxhc3ROYW1lPC0iV2l0a293ZXIiCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgQWZ0ZXIgYXNzaWduaW5nIGEgbmFtZSBhYm92ZSwgcnVuIHRoZSByZXN0IG9mIHRoZSBjb2RlIHRvIGNyZWF0ZSB0aGUgZnVuY3Rpb246ICMjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKI2xvYWQgbGlicmFyaWVzCnJlcXVpcmUoc2Nob2xhcikKcmVxdWlyZSh0aWJibGUpIApyZXF1aXJlKHRtKQpyZXF1aXJlKHRpZHl0ZXh0KQpyZXF1aXJlKHRpZHl2ZXJzZSkKcmVxdWlyZSh3b3JkY2xvdWQyKQpyZXF1aXJlKHJhbmRvbWNvbG9SKSAKCiNDcmVhdGUgZnVuY3Rpb24KR29vZ2xlU2Nob2xhcldvcmRDbG91ZDwtZnVuY3Rpb24oZmlyc3QsbGFzdCl7CiAgCiAgI0dldCBJRCBudW1iZXIgZnJvbSBuYW1lCiAgSURudW1iZXI8LWdldF9zY2hvbGFyX2lkKGZpcnN0X25hbWUgPSBmaXJzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RfbmFtZSA9IGxhc3QpCiAgCiAgI0dldCBwdWJsaWNhdGlvbnMKICBNeXB1YmxpY2F0aW9uczwtdGliYmxlKGdldF9wdWJsaWNhdGlvbnMoSURudW1iZXIpKQogIAogICNBZGQgYWJzdHJhY3RzCiAgTXlwdWJsaWNhdGlvbnMkYWJzdHJhY3Q8LXJlcCgiaW5pdGlhbGl6ZSIsIG5yb3coTXlwdWJsaWNhdGlvbnMpKQogIGZvciAoaSBpbiAxOm5yb3coTXlwdWJsaWNhdGlvbnMpKSB7CiAgICBBYnN0cmFjdHMgPC0gZ2V0X3B1YmxpY2F0aW9uX2Fic3RyYWN0KGlkID0gSURudW1iZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWJfaWQgPSBNeXB1YmxpY2F0aW9ucyRwdWJpZFtpXSkKICAgIGlmZWxzZShsZW5ndGgoQWJzdHJhY3RzKSA9PSAwLCBOQSwgTXlwdWJsaWNhdGlvbnMkYWJzdHJhY3RbaV0gPC0gQWJzdHJhY3RzKSAKICB9CiAgCiAgI0NyZWF0ZSBDb3JwdXMKICBNeUFic3RyYWN0cy5jb3JwdXM8LVZDb3JwdXMoVmVjdG9yU291cmNlKE15cHVibGljYXRpb25zJGFic3RyYWN0KSkKICAKICAjQ3JlYXRlIFRETSBmcm9tIGNvcnB1cwogIE15QWJzdHJhY3RzLlRETTwtVGVybURvY3VtZW50TWF0cml4KE15QWJzdHJhY3RzLmNvcnB1cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHJlbW92ZVB1bmN0dWF0aW9uID0gVFJVRSwgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wd29yZHMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2xvd2VyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlbW1pbmcgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW1vdmVOdW1iZXJzID0gVFJVRSkpCiAgI1RpZHkgVERNCiAgTXlBYnN0cmFjdHMuVERNPC10aWR5KE15QWJzdHJhY3RzLlRETSkgJT4lIAogICAgZ3JvdXBfYnkodGVybSkgJT4lIAogICAgc3VtbWFyaXNlKGNvdW50LGNvdW50ID0gc3VtKGNvdW50KSkgJT4lIAogICAgdW5pcXVlKCklPiUgIAogICAgdW5ncm91cCgpICU+JQogICAgYXJyYW5nZShkZXNjKGNvdW50KSkgICAgCiAgCiAgI3JldGFpbiB0b3AgMTAwIG1vc3QgZnJlcXVlbnRseSB1c2VkIHdvcmRzCiAgTXlBYnN0cmFjdHMuVERNPC1oZWFkKE15QWJzdHJhY3RzLlRETSwgMTAwKSAKICAKICAjY3JlYXRlIHdvcmRjbG91ZAogIHdvcmRjbG91ZDwtd29yZGNsb3VkMihNeUFic3RyYWN0cy5URE0sCiAgICAgICAgICAgICBmb250V2VpZ2h0ID0gImJvbGQiLCAgI2JvbGQgYWxsIGl0ZW1zCiAgICAgICAgICAgICByb3RhdGUgPSAwLCAgICAgICAgICAgI21heCByb3RhdGlvbiBvZiB3b3JkcyAKICAgICAgICAgICAgIHNpemU9Ljc1LCAgICAgICAgICAgICAjc2l6ZSBvZiB3b3JkY2xvdWQKICAgICAgICAgICAgIGZvbnRGYW1pbHkgPSAiVGltZXMiLCAjZm9udCBvZiB3b3JkY2xvdWQgICAKICAgICAgICAgICAgIGNvbG9yID0gcmFuZG9tQ29sb3IobnJvdyhNeUFic3RyYWN0cy5URE0pLCAjY3JlYXRlIGNvbG9yIHBhbGV0dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHVlID0gInJhbmRvbSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsdW1pbm9zaXR5ID0gImRhcmsiKSkgCiAgCiAgcmV0dXJuKHdvcmRjbG91ZCkKICB9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIFJ1biB0aGUgZnVuY3Rpb24gd2UganVzdCBjcmVhdGVkLCB1c2luZyB0aGUgYXNzaWduZWQgbmFtZSAjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpHb29nbGVTY2hvbGFyV29yZENsb3VkKEZpcnN0TmFtZSwgTGFzdE5hbWUpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIEVuZC4gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmBgYA==