SOLUTIONS


Tutorial 2, Advanced Crime Analysis, BSc Security and Crime Science, UCL


Aim of this tutorial

This tutorial will help you consolidate some techniques presented and used earlier in this module about APIs and web-scraping. You will also be able to start working on your own webscraping programme that might be useful for your final project.

Task 1: Geographical variation of public perception on Twitter

Use Twitter’s API to retrieve Tweets about “crime” in these cities: (1) New York City. (2) London, (3) Los Angeles, (4) Austin, Texas, and (5) Dublin.

Store all tweets in a single dataframe with a column identifying the city.

#your code comes here

Task 2: Using YouTube’s API to analyse highly controversial content

Recently, the razor manufacturer Gilette released a video called We Believe: The Best Men Can Be. That video has been extremely controversial and has evoked a number considerably opinioted responses.

Use YouTube’s API to gether information about that video. Note: retrieving all comments will exceed your quota and will take a very long time.

#your code comes here

Task 3: Retrieving the public opinion on that controversial topic.

You might not that some comments below that video are on the boundary of hate speech or even in full-blown aggressive language.

Let’s look at a different source of opinion about that video. This recent opinion article in the Guardian discusses the video.

Try to access the comments made to that video and store them in a dataframe along with a unique identifier.

#your code comes here

Task 4: Recreate the lyrics scraping example with your own artist selection

In this blog you can see stepwise how to scrape data from popular music artists and then access the lyrics of their songs.

(Re-)use the code from the above blog post and re-do their scraping process using your own artist (charts) selection.

#NOTE: Differences in html node names - CORRECTED
#NOTE: Found that artists included featuring... which meant I got 404 errors - CORRECTED

#Load Packages
library(tidyverse)
library(rvest)

#Identify the url from where you want to extract data
URLBase<-"https://www.billboard.com/charts/hot-100-60th-anniversary"
#Read HTML of that page
pageBase<-read_html(URLBase)

#Get the artist name
artist<-html_nodes(pageBase, ".chart-list-item__artist")
#Convert to char
artist<-as.character(html_text(artist))

#Get the artist rank
artistRank<-html_nodes(pageBase, ".chart-list-item__rank ")
#Convert to numeric
artistRank<-as.numeric(html_text(artistRank))

# Save it to a tibble df
  #Note how we replace the '\n' character (which means newline) with nothing
  #Then we filter for top 10
artistsTop<-tibble('Artist' = gsub("\n", "", artist),
                      'Rank' = artistRank) %>%
                  filter(artistRank <= 10)

#Format the link to navigate to the artists genius webpage
URLGenius<-paste0("https://genius.com/artists/",artistsTop$Artist)

#Issues aries when songs in the chart are from one artist featuring another
#Because these collaborations do not have their own artist page
#We therefore consider the main artist only

#Edit song link
  #Remove everything after the word Featuring
URLGenius<-lapply(URLGenius, function(x) {sub('Featuring.*', '', x)})
#Unlist so in same format
URLGenius<-unlist(URLGenius)

#Initialize a tibble df to store the results
artistLyrics<-tibble()

#Retrieve song links for each artist 
for (i in 1:10) {
  pageGenius<-read_html(URLGenius[i])
  URLSongs <- html_nodes(pageGenius, ".mini_card_grid-song a") %>%
      html_attr("href") 
  
   #Inner loop to get the Song Name and Lyrics from the Song Link    
    for (j in 1:10) {
      
      # Get lyrics
      songLyrics <- read_html(URLSongs[j]) %>%
          html_nodes("div.lyrics p") %>%
          html_text()
        
      # Get song name
      songName <- read_html(URLSongs[j]) %>%
         html_nodes("h1.header_with_cover_art-primary_info-title") %>%
         html_text()
        
      # Save the details to a tibble df
      artistLyrics <- rbind(artistLyrics, tibble(Rank = artistsTop$Rank[i],
                                                   Artist = artistsTop$Artist[i],
                                                   Song = songName,
                                                   Lyrics = songLyrics ))
      
      # Insert time delay
      Sys.sleep(10)
     }
} 

#View results
artistLyrics

Task 5: Creating a local database of missing persons

The problem of missing persons in the UK is increasingly recognised in academic research. However, to date not curated database exists that researchers can easily download to query the data of people reported missing.

Note that some of the images are from dead persons and might be confrontational to look at.

Your task is to create a local database on your computer of missing females. Use this urlc (https://www.missingpersons.police.uk/en-gb/case-search/9444442) as a starting point and retrieve the (1) gender, (2) age, (3) ethnicity and (4) circumstances details of the first three pages of search results.

#your code comes here
#Takes the base URL and pastes with 1,2,3 to get a list of pages to search
#Each page is then searched for the 'View Case Details' buttons and the hyperlinks noted
URLMissingPersons <- lapply(paste0('https://www.missingpersons.police.uk/en-gb/case-search/9444442?orderBy=dateDesc&page=', 1:3),
                function(url){
                    url %>% read_html() %>% 
                        html_nodes(".Case") %>%
                        html_nodes(".btn-default") %>%
                        html_attr('href')
                })
#View Output
URLMissingPersons

#Unlist output
#Simplify as we have nensted lists: one for each page
URLMissingPersons<-unlist(URLMissingPersons)

#View Output
URLMissingPersons

#Add base URL to get complete URL
URLMissingPersons[] <- lapply(URLMissingPersons, function(x) paste("https://www.missingpersons.police.uk/en-gb", x, sep=""))

#Unlist
URLMissingPersons<-unlist(URLMissingPersons)

#ViewOutput
URLMissingPersons
#Extract Case Details Section
#Uses lapply to apply the following function to every URL in our case list
  #Extract a list of the case detail field titles
  #Extract a list of the case details field details
  #Bind the two together
  #Return a dataframe

dbMissingPersons<- lapply(URLMissingPersons,
                          function(url){
                            caseDetailsTitle<- url %>% read_html() %>%
                              html_nodes('dl.CaseDetails dt') %>%
                              html_text()
                            caseDetailsDescription<-url %>% read_html() %>%
                              html_nodes('dl.CaseDetails dd') %>%
                              html_text()
          
                            caseDetails<-data.frame(caseDetailsTitle, caseDetailsDescription)
                            
                            return(caseDetails)
                          })

#ViewOutput
View(dbMissingPersons)

Task 6: Web-scraping for the detection of potentially suspicious items

You can use web-scraping to look for suspicious items on online market places. Let’s take the example of clothing from the “Supreme” brand on gumtree.

You can access the search results for supreme hoodie here: https://www.gumtree.com/search?featured_filter=false&urgent_filter=false&sort=date&search_scope=false&photos_filter=false&search_category=all&q=supreme+hoodie&tq=%7B%22i%22%3A%22supreme%22%2C%22s%22%3A%22supreme+hoodie%22%2C%22p%22%3A9%2C%22t%22%3A14%7D&search_location=

Your task now is to scrape the (1) price, (2) title, and (3) location of each ad.

#your code comes here

#acces gumtree page
target_url = "https://www.gumtree.com/search?featured_filter=false&urgent_filter=false&sort=date&search_scope=false&photos_filter=false&search_category=all&q=supreme+hoodie&tq=%7B%22i%22%3A%22supreme%22%2C%22s%22%3A%22supreme+hoodie%22%2C%22p%22%3A9%2C%22t%22%3A14%7D&search_location="
target_page = read_html(target_url)
# look for page structure and find products"
ProductListings = target_page %>% html_nodes(xpath = '//*[@id="srp-results"]') %>% html_nodes("div.srp-results") %>% html_nodes("div.grid-row") %>% html_nodes("div.space-mbxs:nth-child(3)") %>% html_nodes(".list-listing-mini") %>% html_nodes("li")
# contents for products
SingleProds = ProductListings %>% html_nodes("article") %>% html_nodes("a.listing-link") %>% html_nodes("div.listing-content")
# title, price, location of products
ProductsTitle = SingleProds %>% html_node("h2.listing-title") %>% html_text()
ProductsPrice = SingleProds %>% html_node("span.listing-price") %>% html_text()
ProductsLocation = SingleProds %>% html_node("div.listing-location") %>% html_text()
# save data in data frame
AllData = data.frame(ProductsTitle, ProductsPrice, ProductsLocation) 

Task 7: Start with your own web-scraping

Choose a website that you want to scrape (e.g. for your final project for this module). In this task, try to understand the structure of that website and how you can retrieve the content you wish to access.

Write a stepwise plan on what you need to do to obtain that data.

#write your plan here

#step1:
#step2:
#step3:
#step4:
#step5:

Now start by creating a my_target_url variable and do the initial accessing of that url with the read_html function:

#your code comes here

Task 8: Your custom web-scraper

Start building your own webscraper here:

#your code comes here
LS0tCnRpdGxlOiAiU29sdXRpb25zOiBXZWJzY3JhcGluZyBpbiBSIgphdXRob3I6ICJCIEtsZWluYmVyZywgRiBTb2xkbmVyLCBEIEhhbW1vY2tzIgpkYXRlOiAyMiBKYW51YXJ5IDIwMTkKc3VidGl0bGU6IERlcHQgb2YgU2VjdXJpdHkgYW5kIENyaW1lIFNjaWVuY2UsIFVDTApvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoqKlNPTFVUSU9OUyoqCgotLS0KClR1dG9yaWFsIDIsIEFkdmFuY2VkIENyaW1lIEFuYWx5c2lzLCBCU2MgU2VjdXJpdHkgYW5kIENyaW1lIFNjaWVuY2UsIFVDTAoKLS0tCgojIyBBaW0gb2YgdGhpcyB0dXRvcmlhbAoKVGhpcyB0dXRvcmlhbCB3aWxsIGhlbHAgeW91IGNvbnNvbGlkYXRlIHNvbWUgdGVjaG5pcXVlcyBwcmVzZW50ZWQgYW5kIHVzZWQgZWFybGllciBpbiB0aGlzIG1vZHVsZSBhYm91dCBBUElzIGFuZCB3ZWItc2NyYXBpbmcuIFlvdSB3aWxsIGFsc28gYmUgYWJsZSB0byBzdGFydCB3b3JraW5nIG9uIHlvdXIgb3duIHdlYnNjcmFwaW5nIHByb2dyYW1tZSB0aGF0IG1pZ2h0IGJlIHVzZWZ1bCBmb3IgeW91ciBmaW5hbCBwcm9qZWN0LgoKCiMjIFRhc2sgMTogR2VvZ3JhcGhpY2FsIHZhcmlhdGlvbiBvZiBwdWJsaWMgcGVyY2VwdGlvbiBvbiBUd2l0dGVyCgpVc2UgVHdpdHRlcidzIEFQSSB0byByZXRyaWV2ZSBUd2VldHMgYWJvdXQgImNyaW1lIiBpbiB0aGVzZSBjaXRpZXM6ICgxKSBOZXcgWW9yayBDaXR5LiAoMikgTG9uZG9uLCAoMykgTG9zIEFuZ2VsZXMsICg0KSBBdXN0aW4sIFRleGFzLCBhbmQgKDUpIER1Ymxpbi4KClN0b3JlIGFsbCB0d2VldHMgaW4gYSBzaW5nbGUgZGF0YWZyYW1lIHdpdGggYSBjb2x1bW4gaWRlbnRpZnlpbmcgdGhlIGNpdHkuCgpgYGB7cn0KI3lvdXIgY29kZSBjb21lcyBoZXJlCmBgYAoKCiMjIFRhc2sgMjogVXNpbmcgWW91VHViZSdzIEFQSSB0byBhbmFseXNlIGhpZ2hseSBjb250cm92ZXJzaWFsIGNvbnRlbnQKClJlY2VudGx5LCB0aGUgcmF6b3IgbWFudWZhY3R1cmVyIEdpbGV0dGUgcmVsZWFzZWQgYSB2aWRlbyBjYWxsZWQgW1dlIEJlbGlldmU6IFRoZSBCZXN0IE1lbiBDYW4gQmVdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9a29QbXVFeVAzYTApLiBUaGF0IHZpZGVvIGhhcyBiZWVuIGV4dHJlbWVseSBjb250cm92ZXJzaWFsIGFuZCBoYXMgZXZva2VkIGEgbnVtYmVyIGNvbnNpZGVyYWJseSBvcGluaW90ZWQgcmVzcG9uc2VzLgoKVXNlIFlvdVR1YmUncyBBUEkgdG8gZ2V0aGVyIGluZm9ybWF0aW9uIGFib3V0IHRoYXQgdmlkZW8uIF9Ob3RlOiByZXRyaWV2aW5nIGFsbCBjb21tZW50cyB3aWxsIGV4Y2VlZCB5b3VyIHF1b3RhIGFuZCB3aWxsIHRha2UgYSB2ZXJ5IGxvbmcgdGltZS5fCgpgYGB7cn0KI3lvdXIgY29kZSBjb21lcyBoZXJlCmBgYAoKCiMjIFRhc2sgMzogUmV0cmlldmluZyB0aGUgcHVibGljIG9waW5pb24gb24gdGhhdCBjb250cm92ZXJzaWFsIHRvcGljLgoKWW91IG1pZ2h0IG5vdCB0aGF0IHNvbWUgY29tbWVudHMgYmVsb3cgdGhhdCB2aWRlbyBhcmUgb24gdGhlIGJvdW5kYXJ5IG9mIGhhdGUgc3BlZWNoIG9yIGV2ZW4gaW4gZnVsbC1ibG93biBhZ2dyZXNzaXZlIGxhbmd1YWdlLgoKTGV0J3MgbG9vayBhdCBhIGRpZmZlcmVudCBzb3VyY2Ugb2Ygb3BpbmlvbiBhYm91dCB0aGF0IHZpZGVvLiBUaGlzIFtyZWNlbnQgb3BpbmlvbiBhcnRpY2xlXShodHRwczovL3d3dy50aGVndWFyZGlhbi5jb20vY29tbWVudGlzZnJlZS8yMDE5L2phbi8xNi9tZW4tbWFzY3VsaW5pdHktZ2lsbGV0dGUtYWR2ZXJ0aXNlbWVudCkgaW4gdGhlIEd1YXJkaWFuIGRpc2N1c3NlcyB0aGUgdmlkZW8uCgpUcnkgdG8gYWNjZXNzIHRoZSBjb21tZW50cyBtYWRlIHRvIHRoYXQgdmlkZW8gYW5kIHN0b3JlIHRoZW0gaW4gYSBkYXRhZnJhbWUgYWxvbmcgd2l0aCBhIHVuaXF1ZSBpZGVudGlmaWVyLgoKYGBge3J9CiN5b3VyIGNvZGUgY29tZXMgaGVyZQpgYGAKCgojIyBUYXNrIDQ6IFJlY3JlYXRlIHRoZSBseXJpY3Mgc2NyYXBpbmcgZXhhbXBsZSB3aXRoIHlvdXIgb3duIGFydGlzdCBzZWxlY3Rpb24KCkluIFt0aGlzIGJsb2ddKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9sZWFybi10by1jcmVhdGUteW91ci1vd24tZGF0YXNldHMtd2ViLXNjcmFwaW5nLWluLXItZjkzNGEzMTc0OGE1KSB5b3UgY2FuIHNlZSBzdGVwd2lzZSBob3cgdG8gc2NyYXBlIGRhdGEgZnJvbSBwb3B1bGFyIG11c2ljIGFydGlzdHMgYW5kIHRoZW4gYWNjZXNzIHRoZSBseXJpY3Mgb2YgdGhlaXIgc29uZ3MuCgooUmUtKXVzZSB0aGUgY29kZSBmcm9tIHRoZSBhYm92ZSBibG9nIHBvc3QgYW5kIHJlLWRvIHRoZWlyIHNjcmFwaW5nIHByb2Nlc3MgdXNpbmcgeW91ciBvd24gYXJ0aXN0IChjaGFydHMpIHNlbGVjdGlvbi4KCmBgYHtyfQojTk9URTogRGlmZmVyZW5jZXMgaW4gaHRtbCBub2RlIG5hbWVzIC0gQ09SUkVDVEVECiNOT1RFOiBGb3VuZCB0aGF0IGFydGlzdHMgaW5jbHVkZWQgZmVhdHVyaW5nLi4uIHdoaWNoIG1lYW50IEkgZ290IDQwNCBlcnJvcnMgLSBDT1JSRUNURUQKCiNMb2FkIFBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJ2ZXN0KQoKI0lkZW50aWZ5IHRoZSB1cmwgZnJvbSB3aGVyZSB5b3Ugd2FudCB0byBleHRyYWN0IGRhdGEKVVJMQmFzZTwtImh0dHBzOi8vd3d3LmJpbGxib2FyZC5jb20vY2hhcnRzL2hvdC0xMDAtNjB0aC1hbm5pdmVyc2FyeSIKI1JlYWQgSFRNTCBvZiB0aGF0IHBhZ2UKcGFnZUJhc2U8LXJlYWRfaHRtbChVUkxCYXNlKQoKI0dldCB0aGUgYXJ0aXN0IG5hbWUKYXJ0aXN0PC1odG1sX25vZGVzKHBhZ2VCYXNlLCAiLmNoYXJ0LWxpc3QtaXRlbV9fYXJ0aXN0IikKI0NvbnZlcnQgdG8gY2hhcgphcnRpc3Q8LWFzLmNoYXJhY3RlcihodG1sX3RleHQoYXJ0aXN0KSkKCiNHZXQgdGhlIGFydGlzdCByYW5rCmFydGlzdFJhbms8LWh0bWxfbm9kZXMocGFnZUJhc2UsICIuY2hhcnQtbGlzdC1pdGVtX19yYW5rICIpCiNDb252ZXJ0IHRvIG51bWVyaWMKYXJ0aXN0UmFuazwtYXMubnVtZXJpYyhodG1sX3RleHQoYXJ0aXN0UmFuaykpCgojIFNhdmUgaXQgdG8gYSB0aWJibGUgZGYKICAjTm90ZSBob3cgd2UgcmVwbGFjZSB0aGUgJ1xuJyBjaGFyYWN0ZXIgKHdoaWNoIG1lYW5zIG5ld2xpbmUpIHdpdGggbm90aGluZwogICNUaGVuIHdlIGZpbHRlciBmb3IgdG9wIDEwCmFydGlzdHNUb3A8LXRpYmJsZSgnQXJ0aXN0JyA9IGdzdWIoIlxuIiwgIiIsIGFydGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAnUmFuaycgPSBhcnRpc3RSYW5rKSAlPiUKICAgICAgICAgICAgICAgICAgZmlsdGVyKGFydGlzdFJhbmsgPD0gMTApCgojRm9ybWF0IHRoZSBsaW5rIHRvIG5hdmlnYXRlIHRvIHRoZSBhcnRpc3RzIGdlbml1cyB3ZWJwYWdlClVSTEdlbml1czwtcGFzdGUwKCJodHRwczovL2dlbml1cy5jb20vYXJ0aXN0cy8iLGFydGlzdHNUb3AkQXJ0aXN0KQoKI0lzc3VlcyBhcmllcyB3aGVuIHNvbmdzIGluIHRoZSBjaGFydCBhcmUgZnJvbSBvbmUgYXJ0aXN0IGZlYXR1cmluZyBhbm90aGVyCiNCZWNhdXNlIHRoZXNlIGNvbGxhYm9yYXRpb25zIGRvIG5vdCBoYXZlIHRoZWlyIG93biBhcnRpc3QgcGFnZQojV2UgdGhlcmVmb3JlIGNvbnNpZGVyIHRoZSBtYWluIGFydGlzdCBvbmx5CgojRWRpdCBzb25nIGxpbmsKICAjUmVtb3ZlIGV2ZXJ5dGhpbmcgYWZ0ZXIgdGhlIHdvcmQgRmVhdHVyaW5nClVSTEdlbml1czwtbGFwcGx5KFVSTEdlbml1cywgZnVuY3Rpb24oeCkge3N1YignRmVhdHVyaW5nLionLCAnJywgeCl9KQojVW5saXN0IHNvIGluIHNhbWUgZm9ybWF0ClVSTEdlbml1czwtdW5saXN0KFVSTEdlbml1cykKCiNJbml0aWFsaXplIGEgdGliYmxlIGRmIHRvIHN0b3JlIHRoZSByZXN1bHRzCmFydGlzdEx5cmljczwtdGliYmxlKCkKCiNSZXRyaWV2ZSBzb25nIGxpbmtzIGZvciBlYWNoIGFydGlzdCAKZm9yIChpIGluIDE6MTApIHsKICBwYWdlR2VuaXVzPC1yZWFkX2h0bWwoVVJMR2VuaXVzW2ldKQogIFVSTFNvbmdzIDwtIGh0bWxfbm9kZXMocGFnZUdlbml1cywgIi5taW5pX2NhcmRfZ3JpZC1zb25nIGEiKSAlPiUKICAgICAgaHRtbF9hdHRyKCJocmVmIikgCiAgCiAgICNJbm5lciBsb29wIHRvIGdldCB0aGUgU29uZyBOYW1lIGFuZCBMeXJpY3MgZnJvbSB0aGUgU29uZyBMaW5rICAgIAogICAgZm9yIChqIGluIDE6MTApIHsKICAgICAgCiAgICAgICMgR2V0IGx5cmljcwogICAgICBzb25nTHlyaWNzIDwtIHJlYWRfaHRtbChVUkxTb25nc1tqXSkgJT4lCiAgICAgICAgICBodG1sX25vZGVzKCJkaXYubHlyaWNzIHAiKSAlPiUKICAgICAgICAgIGh0bWxfdGV4dCgpCiAgICAgICAgCiAgICAgICMgR2V0IHNvbmcgbmFtZQogICAgICBzb25nTmFtZSA8LSByZWFkX2h0bWwoVVJMU29uZ3Nbal0pICU+JQogICAgICAgICBodG1sX25vZGVzKCJoMS5oZWFkZXJfd2l0aF9jb3Zlcl9hcnQtcHJpbWFyeV9pbmZvLXRpdGxlIikgJT4lCiAgICAgICAgIGh0bWxfdGV4dCgpCiAgICAgICAgCiAgICAgICMgU2F2ZSB0aGUgZGV0YWlscyB0byBhIHRpYmJsZSBkZgogICAgICBhcnRpc3RMeXJpY3MgPC0gcmJpbmQoYXJ0aXN0THlyaWNzLCB0aWJibGUoUmFuayA9IGFydGlzdHNUb3AkUmFua1tpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQXJ0aXN0ID0gYXJ0aXN0c1RvcCRBcnRpc3RbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNvbmcgPSBzb25nTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTHlyaWNzID0gc29uZ0x5cmljcyApKQogICAgICAKICAgICAgIyBJbnNlcnQgdGltZSBkZWxheQogICAgICBTeXMuc2xlZXAoMTApCiAgICAgfQp9IAoKI1ZpZXcgcmVzdWx0cwphcnRpc3RMeXJpY3MKYGBgCgojIyBUYXNrIDU6IENyZWF0aW5nIGEgbG9jYWwgZGF0YWJhc2Ugb2YgbWlzc2luZyBwZXJzb25zCgpUaGUgcHJvYmxlbSBvZiBtaXNzaW5nIHBlcnNvbnMgaW4gdGhlIFVLIGlzIGluY3JlYXNpbmdseSByZWNvZ25pc2VkIGluIGFjYWRlbWljIHJlc2VhcmNoLiBIb3dldmVyLCB0byBkYXRlIG5vdCBjdXJhdGVkIGRhdGFiYXNlIGV4aXN0cyB0aGF0IHJlc2VhcmNoZXJzIGNhbiBlYXNpbHkgZG93bmxvYWQgdG8gcXVlcnkgdGhlIGRhdGEgb2YgcGVvcGxlIHJlcG9ydGVkIG1pc3NpbmcuCgpfTm90ZSB0aGF0IHNvbWUgb2YgdGhlIGltYWdlcyBhcmUgZnJvbSBkZWFkIHBlcnNvbnMgYW5kIG1pZ2h0IGJlIGNvbmZyb250YXRpb25hbCB0byBsb29rIGF0Ll8KCllvdXIgdGFzayBpcyB0byBjcmVhdGUgYSBsb2NhbCBkYXRhYmFzZSBvbiB5b3VyIGNvbXB1dGVyIG9mIG1pc3NpbmcgZmVtYWxlcy4gVXNlIHRoaXMgdXJsYyBbKGh0dHBzOi8vd3d3Lm1pc3NpbmdwZXJzb25zLnBvbGljZS51ay9lbi1nYi9jYXNlLXNlYXJjaC85NDQ0NDQyKV0oaHR0cHM6Ly93d3cubWlzc2luZ3BlcnNvbnMucG9saWNlLnVrL2VuLWdiL2Nhc2Utc2VhcmNoLzk0NDQ0NDIpIGFzIGEgc3RhcnRpbmcgcG9pbnQgYW5kIHJldHJpZXZlIHRoZSAoMSkgZ2VuZGVyLCAoMikgYWdlLCAoMykgZXRobmljaXR5IGFuZCAoNCkgY2lyY3Vtc3RhbmNlcyBkZXRhaWxzIG9mIHRoZSBmaXJzdCB0aHJlZSBwYWdlcyBvZiBzZWFyY2ggcmVzdWx0cy4KCmBgYHtyfQojeW91ciBjb2RlIGNvbWVzIGhlcmUKI1Rha2VzIHRoZSBiYXNlIFVSTCBhbmQgcGFzdGVzIHdpdGggMSwyLDMgdG8gZ2V0IGEgbGlzdCBvZiBwYWdlcyB0byBzZWFyY2gKI0VhY2ggcGFnZSBpcyB0aGVuIHNlYXJjaGVkIGZvciB0aGUgJ1ZpZXcgQ2FzZSBEZXRhaWxzJyBidXR0b25zIGFuZCB0aGUgaHlwZXJsaW5rcyBub3RlZApVUkxNaXNzaW5nUGVyc29ucyA8LSBsYXBwbHkocGFzdGUwKCdodHRwczovL3d3dy5taXNzaW5ncGVyc29ucy5wb2xpY2UudWsvZW4tZ2IvY2FzZS1zZWFyY2gvOTQ0NDQ0Mj9vcmRlckJ5PWRhdGVEZXNjJnBhZ2U9JywgMTozKSwKICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHVybCl7CiAgICAgICAgICAgICAgICAgICAgdXJsICU+JSByZWFkX2h0bWwoKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoIi5DYXNlIikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoIi5idG4tZGVmYXVsdCIpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBodG1sX2F0dHIoJ2hyZWYnKQogICAgICAgICAgICAgICAgfSkKYGBgCgpgYGB7cn0KI1ZpZXcgT3V0cHV0ClVSTE1pc3NpbmdQZXJzb25zCgojVW5saXN0IG91dHB1dAojU2ltcGxpZnkgYXMgd2UgaGF2ZSBuZW5zdGVkIGxpc3RzOiBvbmUgZm9yIGVhY2ggcGFnZQpVUkxNaXNzaW5nUGVyc29uczwtdW5saXN0KFVSTE1pc3NpbmdQZXJzb25zKQoKI1ZpZXcgT3V0cHV0ClVSTE1pc3NpbmdQZXJzb25zCgojQWRkIGJhc2UgVVJMIHRvIGdldCBjb21wbGV0ZSBVUkwKVVJMTWlzc2luZ1BlcnNvbnNbXSA8LSBsYXBwbHkoVVJMTWlzc2luZ1BlcnNvbnMsIGZ1bmN0aW9uKHgpIHBhc3RlKCJodHRwczovL3d3dy5taXNzaW5ncGVyc29ucy5wb2xpY2UudWsvZW4tZ2IiLCB4LCBzZXA9IiIpKQoKI1VubGlzdApVUkxNaXNzaW5nUGVyc29uczwtdW5saXN0KFVSTE1pc3NpbmdQZXJzb25zKQoKI1ZpZXdPdXRwdXQKVVJMTWlzc2luZ1BlcnNvbnMKYGBgCgpgYGB7cn0KI0V4dHJhY3QgQ2FzZSBEZXRhaWxzIFNlY3Rpb24KI1VzZXMgbGFwcGx5IHRvIGFwcGx5IHRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gdG8gZXZlcnkgVVJMIGluIG91ciBjYXNlIGxpc3QKICAjRXh0cmFjdCBhIGxpc3Qgb2YgdGhlIGNhc2UgZGV0YWlsIGZpZWxkIHRpdGxlcwogICNFeHRyYWN0IGEgbGlzdCBvZiB0aGUgY2FzZSBkZXRhaWxzIGZpZWxkIGRldGFpbHMKICAjQmluZCB0aGUgdHdvIHRvZ2V0aGVyCiAgI1JldHVybiBhIGRhdGFmcmFtZQoKZGJNaXNzaW5nUGVyc29uczwtIGxhcHBseShVUkxNaXNzaW5nUGVyc29ucywKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih1cmwpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZURldGFpbHNUaXRsZTwtIHVybCAlPiUgcmVhZF9odG1sKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoJ2RsLkNhc2VEZXRhaWxzIGR0JykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfdGV4dCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlRGV0YWlsc0Rlc2NyaXB0aW9uPC11cmwgJT4lIHJlYWRfaHRtbCgpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sX25vZGVzKCdkbC5DYXNlRGV0YWlscyBkZCcpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sX3RleHQoKQogICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlRGV0YWlsczwtZGF0YS5mcmFtZShjYXNlRGV0YWlsc1RpdGxlLCBjYXNlRGV0YWlsc0Rlc2NyaXB0aW9uKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4oY2FzZURldGFpbHMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKCiNWaWV3T3V0cHV0ClZpZXcoZGJNaXNzaW5nUGVyc29ucykKYGBgCgoKCiMjIFRhc2sgNjogV2ViLXNjcmFwaW5nIGZvciB0aGUgZGV0ZWN0aW9uIG9mIHBvdGVudGlhbGx5IHN1c3BpY2lvdXMgaXRlbXMKCllvdSBjYW4gdXNlIHdlYi1zY3JhcGluZyB0byBsb29rIGZvciBzdXNwaWNpb3VzIGl0ZW1zIG9uIG9ubGluZSBtYXJrZXQgcGxhY2VzLiBMZXQncyB0YWtlIHRoZSBleGFtcGxlIG9mIGNsb3RoaW5nIGZyb20gdGhlICJTdXByZW1lIiBicmFuZCBvbiBndW10cmVlLgoKWW91IGNhbiBhY2Nlc3MgdGhlIHNlYXJjaCByZXN1bHRzIGZvciBgc3VwcmVtZSBob29kaWVgIGhlcmU6IFtodHRwczovL3d3dy5ndW10cmVlLmNvbS9zZWFyY2g/ZmVhdHVyZWRfZmlsdGVyPWZhbHNlJnVyZ2VudF9maWx0ZXI9ZmFsc2Umc29ydD1kYXRlJnNlYXJjaF9zY29wZT1mYWxzZSZwaG90b3NfZmlsdGVyPWZhbHNlJnNlYXJjaF9jYXRlZ29yeT1hbGwmcT1zdXByZW1lK2hvb2RpZSZ0cT0lN0IlMjJpJTIyJTNBJTIyc3VwcmVtZSUyMiUyQyUyMnMlMjIlM0ElMjJzdXByZW1lK2hvb2RpZSUyMiUyQyUyMnAlMjIlM0E5JTJDJTIydCUyMiUzQTE0JTdEJnNlYXJjaF9sb2NhdGlvbj1dKGh0dHBzOi8vd3d3Lmd1bXRyZWUuY29tL3NlYXJjaD9mZWF0dXJlZF9maWx0ZXI9ZmFsc2UmdXJnZW50X2ZpbHRlcj1mYWxzZSZzb3J0PWRhdGUmc2VhcmNoX3Njb3BlPWZhbHNlJnBob3Rvc19maWx0ZXI9ZmFsc2Umc2VhcmNoX2NhdGVnb3J5PWFsbCZxPXN1cHJlbWUraG9vZGllJnRxPSU3QiUyMmklMjIlM0ElMjJzdXByZW1lJTIyJTJDJTIycyUyMiUzQSUyMnN1cHJlbWUraG9vZGllJTIyJTJDJTIycCUyMiUzQTklMkMlMjJ0JTIyJTNBMTQlN0Qmc2VhcmNoX2xvY2F0aW9uPSkKCllvdXIgdGFzayBub3cgaXMgdG8gc2NyYXBlIHRoZSAoMSkgcHJpY2UsICgyKSB0aXRsZSwgYW5kICgzKSBsb2NhdGlvbiBvZiBlYWNoIGFkLgoKYGBge3J9CiN5b3VyIGNvZGUgY29tZXMgaGVyZQoKI2FjY2VzIGd1bXRyZWUgcGFnZQp0YXJnZXRfdXJsID0gImh0dHBzOi8vd3d3Lmd1bXRyZWUuY29tL3NlYXJjaD9mZWF0dXJlZF9maWx0ZXI9ZmFsc2UmdXJnZW50X2ZpbHRlcj1mYWxzZSZzb3J0PWRhdGUmc2VhcmNoX3Njb3BlPWZhbHNlJnBob3Rvc19maWx0ZXI9ZmFsc2Umc2VhcmNoX2NhdGVnb3J5PWFsbCZxPXN1cHJlbWUraG9vZGllJnRxPSU3QiUyMmklMjIlM0ElMjJzdXByZW1lJTIyJTJDJTIycyUyMiUzQSUyMnN1cHJlbWUraG9vZGllJTIyJTJDJTIycCUyMiUzQTklMkMlMjJ0JTIyJTNBMTQlN0Qmc2VhcmNoX2xvY2F0aW9uPSIKdGFyZ2V0X3BhZ2UgPSByZWFkX2h0bWwodGFyZ2V0X3VybCkKYGBgCgpgYGB7cn0KIyBsb29rIGZvciBwYWdlIHN0cnVjdHVyZSBhbmQgZmluZCBwcm9kdWN0cyIKUHJvZHVjdExpc3RpbmdzID0gdGFyZ2V0X3BhZ2UgJT4lIGh0bWxfbm9kZXMoeHBhdGggPSAnLy8qW0BpZD0ic3JwLXJlc3VsdHMiXScpICU+JSBodG1sX25vZGVzKCJkaXYuc3JwLXJlc3VsdHMiKSAlPiUgaHRtbF9ub2RlcygiZGl2LmdyaWQtcm93IikgJT4lIGh0bWxfbm9kZXMoImRpdi5zcGFjZS1tYnhzOm50aC1jaGlsZCgzKSIpICU+JSBodG1sX25vZGVzKCIubGlzdC1saXN0aW5nLW1pbmkiKSAlPiUgaHRtbF9ub2RlcygibGkiKQpgYGAKCmBgYHtyfQojIGNvbnRlbnRzIGZvciBwcm9kdWN0cwpTaW5nbGVQcm9kcyA9IFByb2R1Y3RMaXN0aW5ncyAlPiUgaHRtbF9ub2RlcygiYXJ0aWNsZSIpICU+JSBodG1sX25vZGVzKCJhLmxpc3RpbmctbGluayIpICU+JSBodG1sX25vZGVzKCJkaXYubGlzdGluZy1jb250ZW50IikKYGBgCgpgYGB7cn0KIyB0aXRsZSwgcHJpY2UsIGxvY2F0aW9uIG9mIHByb2R1Y3RzClByb2R1Y3RzVGl0bGUgPSBTaW5nbGVQcm9kcyAlPiUgaHRtbF9ub2RlKCJoMi5saXN0aW5nLXRpdGxlIikgJT4lIGh0bWxfdGV4dCgpClByb2R1Y3RzUHJpY2UgPSBTaW5nbGVQcm9kcyAlPiUgaHRtbF9ub2RlKCJzcGFuLmxpc3RpbmctcHJpY2UiKSAlPiUgaHRtbF90ZXh0KCkKUHJvZHVjdHNMb2NhdGlvbiA9IFNpbmdsZVByb2RzICU+JSBodG1sX25vZGUoImRpdi5saXN0aW5nLWxvY2F0aW9uIikgJT4lIGh0bWxfdGV4dCgpCmBgYAoKYGBge3J9CiMgc2F2ZSBkYXRhIGluIGRhdGEgZnJhbWUKQWxsRGF0YSA9IGRhdGEuZnJhbWUoUHJvZHVjdHNUaXRsZSwgUHJvZHVjdHNQcmljZSwgUHJvZHVjdHNMb2NhdGlvbikgCmBgYAoKCiMjIFRhc2sgNzogU3RhcnQgd2l0aCB5b3VyIG93biB3ZWItc2NyYXBpbmcKCkNob29zZSBhIHdlYnNpdGUgdGhhdCB5b3Ugd2FudCB0byBzY3JhcGUgKGUuZy4gZm9yIHlvdXIgZmluYWwgcHJvamVjdCBmb3IgdGhpcyBtb2R1bGUpLiBJbiB0aGlzIHRhc2ssIHRyeSB0byB1bmRlcnN0YW5kIHRoZSBzdHJ1Y3R1cmUgb2YgdGhhdCB3ZWJzaXRlIGFuZCBob3cgeW91IGNhbiByZXRyaWV2ZSB0aGUgY29udGVudCB5b3Ugd2lzaCB0byBhY2Nlc3MuCgpXcml0ZSBhIHN0ZXB3aXNlIHBsYW4gb24gd2hhdCB5b3UgbmVlZCB0byBkbyB0byBvYnRhaW4gdGhhdCBkYXRhLgoKYGBge3J9CiN3cml0ZSB5b3VyIHBsYW4gaGVyZQoKI3N0ZXAxOgojc3RlcDI6CiNzdGVwMzoKI3N0ZXA0Ogojc3RlcDU6CmBgYAoKCk5vdyBzdGFydCBieSBjcmVhdGluZyBhIGBteV90YXJnZXRfdXJsYCB2YXJpYWJsZSBhbmQgZG8gdGhlIGluaXRpYWwgYWNjZXNzaW5nIG9mIHRoYXQgdXJsIHdpdGggdGhlIGByZWFkX2h0bWxgIGZ1bmN0aW9uOgoKYGBge3J9CiN5b3VyIGNvZGUgY29tZXMgaGVyZQpgYGAKCgojIyBUYXNrIDg6IFlvdXIgY3VzdG9tIHdlYi1zY3JhcGVyCgpTdGFydCBidWlsZGluZyB5b3VyIG93biB3ZWJzY3JhcGVyIGhlcmU6CgpgYGB7cn0KI3lvdXIgY29kZSBjb21lcyBoZXJlCmBgYAoKCg==