If any issues, questions or suggestions feel free to reach me out via e-mail or Linkedin. You can also visit my Github.

if(!require(pacman)) install.packages('pacman')
pacman::p_load(XML, httr, maps, stringr, dplyr, ggplot2)

This case study originates from the book “Automated Data Collection with R: A Practical Guide to Web Scraping and Text Mining”, written by Munzert, Rubba, Meißner, Nyhuis, published by Wiley in 2015.

As an excercise I try to recreate case study from that book, but with the tidyverse approach to data wrangling and ggplot2 data vizualization. Results may differ since I recreate the results in February 2022 with updated dataset.

Moreover, I explore few more things which in the book were left as an excercise to the reader, i.e. countries with most endangered sites and places which were endangered in the past but now aren’t endangered

Endangered sites (results presented in the book, but recreated with dplyr and ggplot2)

## Get data from the web ##
url <- GET('http://en.wikipedia.org/wiki/List_of_World_Heritage_in_Danger')
tables <- readHTMLTable(rawToChar(url$content)
                        ,stringsAsFactors = FALSE)

## Select 2nd table and get rid of headers from 1st row and select columns ##
dangerTable <- tables[[2]] %>%
  .[-1, c(1,3,4,6,7)]

## Add custom headers ##
colnames(dangerTable) <- c('name'
                           ,'location'
                           ,'criteria'
                           ,'yearInscribed'
                           ,'yearEndangered')

## Some features wrangling ##
dangerTable <- dangerTable %>%
  mutate(criteria = if_else(str_detect(criteria
                                       ,'Natural')
                            ,'Natural'
                            ,'Cultural')) %>%
  mutate(yearInscribed = as.numeric(yearInscribed)) %>%
  mutate(yearEndangered = str_extract_all(yearEndangered
                                          ,pattern = '[0-9]{4}–$') %>%
           unlist() %>%
           str_replace(pattern = '–'
                       ,replacement = '') %>%
           as.numeric()) %>%
## Extracting coordinates ##
  mutate(xCoords = str_extract(location
                               ,pattern = '[;][ -]*[0-9]*[.]*[0-9]*') %>%
           str_sub(start = 3
                   ,end = -1) %>%
           as.numeric() %>%
           round(2)) %>%
  mutate(yCoords = str_extract(location
                              ,pattern = '[/][ -]*[0-9]*[.]*[0-9]*[;]') %>%
           str_sub(start = 3
                   ,end = -2) %>%
           as.numeric() %>%
           round(2))

## World map of endangered places ##
world <- map_data('world')
ggplot() +
  geom_map(data = world
           ,map = world
           ,aes(long
                ,lat
                ,map_id = region)
           ,color = 'white'
           ,fill = 'lightgray') +
  geom_point(data = dangerTable
             ,aes(xCoords
                  ,yCoords
                  ,shape = criteria)
             ,color = 'red'
             ,size = 2.5) +
  scale_shape_manual(values = c(16,17)) +
  labs(title = 'Endangered places in February 2022') +
  theme_void()


## Distribution of years when places become endangered ##
ggplot(dangerTable) +
  geom_histogram(aes(x = yearEndangered)
                 ,binwidth = 5
                 ,color = 'black'
                 ,fill = 'white') +
  scale_x_continuous(breaks = seq(1980, 2025, by = 5)) +
  labs(x = ''
       ,y = ''
       ,title = 'Year when site was put on the list of endangered sites')


## Years it took to become an endangered site ##
dangerTable <- dangerTable %>%
  mutate(duration = yearEndangered - yearInscribed)

ggplot(dangerTable) +
  geom_histogram(aes(x = duration)
                 ,binwidth = 5
                 ,color = 'black'
                 ,fill = 'white') +
  scale_x_continuous(breaks = seq(0, 35, by = 5)) +
  labs(x = ''
       ,y = ''
       ,title = 'Years it took to become an endangered site')

Which countries has the most endangered sites?

Join with map_world from ggplot2 seems not working here, so I use map.where from maps package, but still we obtain few NA’s which probably should have been checked manually at google maps or other similar services.

dangerTable <- dangerTable %>%
  mutate(country = map.where(x = xCoords
                             ,y = yCoords))

countPerCountry <- dangerTable %>%
  group_by(country) %>%
  summarize(count = n())

ggplot(na.omit(countPerCountry)) +
  geom_col(aes(x = reorder(country, count)
               ,y = count)) +
  coord_flip() +
  labs(x = ''
      , y = '')

Sites endangered in the past and comparison with the currently endangered sites

## Select 4th table and get rid of headers from 1st row and select columns ##
prevDangerTable <- tables[[4]] %>%
  .[-1, c(1,3,4,6,7)]

## Add custom headers ##
colnames(prevDangerTable) <- colnames(dangerTable)[1:5]

## Some features wrangling ##
prevDangerTable <- prevDangerTable %>%
  mutate(criteria = if_else(str_detect(criteria
                                       ,'Natural')
                            ,'Natural'
                            ,'Cultural')) %>%
  mutate(yearInscribed = as.numeric(yearInscribed)) %>%
  mutate(endangerStarted = str_extract_all(yearEndangered
                                           ,pattern = '^[0-9]{4}') %>%
           unlist() %>%
           as.numeric()) %>%
  mutate(endangerEnded = str_extract_all(yearEndangered #tak naprawde endanger ended
                                          ,pattern = '[0-9]{4}$') %>%
           unlist() %>%
           as.numeric()) %>%
## Extracting coordinates and countries##
  mutate(xCoords = str_extract(location
                               ,pattern = '[;][ -]*[0-9]*[.]*[0-9]*') %>%
           str_sub(start = 3
                   ,end = -1) %>%
           as.numeric() %>%
           round(2)) %>%
  mutate(yCoords = str_extract(location
                               ,pattern = '[/][ -]*[0-9]*[.]*[0-9]*[;]') %>%
           str_sub(start = 3
                   ,end = -2) %>%
           as.numeric() %>%
           round(2)) %>%
  mutate(country = map.where(x = xCoords
                             ,y = yCoords))

## How much sites disappeared from the list ##
ggplot(prevDangerTable) +
  geom_histogram(aes(endangerEnded)
                 ,binwidth = 5
                 ,color = 'black'
                 ,fill = 'white') +
  scale_x_continuous(breaks = seq(1980, 2025, by = 5)) +
  labs(x = ''
       ,y = ''
       ,title = 'Effectivenes of World Heritage Committee')


## Average endagerment time ##
prevDangerTable <- prevDangerTable %>%
  mutate(endangerDuration = endangerEnded - endangerStarted)

ggplot(prevDangerTable) +
  geom_histogram(aes(endangerDuration)
                 ,binwidth = 5
                 ,color = 'black'
                 ,fill = 'white') +
  scale_x_continuous(breaks = seq(0, 25, by = 5)) +
  labs(x = ''
       ,y = ''
       ,title = 'Endanger duration') +
  annotate('text'
           ,x = 20
           ,y = 10
           ,label = paste0('Average duration \n'
             ,round(mean(prevDangerTable$endangerDuration), 2)
             ,' years')
           ,size = 6)


#### COMPARISON OF CURRENT AND PREVIOUS ENDANGERED PLACES ####
## Merge tables ##
allPlaces <- bind_rows(
  dangerTable %>%
    mutate(endangered = 'Yes')
  ,prevDangerTable %>%
    mutate(endangered = 'No') %>%
    select(-yearEndangered)
)

## World map #$
ggplot() +
  geom_map(data = world
           ,map = world
           ,aes(long
                ,lat
                ,map_id = region)
           ,color = 'white'
           ,fill = 'lightgray') +
  geom_point(data = allPlaces
             ,aes(xCoords
                  ,yCoords
                  ,shape = criteria
                  ,color = endangered)
             ,size = 2.5) +
  scale_shape_manual(values = c(17, 19)) +
  scale_color_manual(values = c('darkgreen', 'red')) +
  labs(title = 'Endangered and previously endangered places in February 2022') +
  theme_void()

LS0tDQp0aXRsZTogIldvcmxkIEhlcml0YWdlIFNpdGVzIGluIERhbmdlciINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCklmIGFueSBpc3N1ZXMsIHF1ZXN0aW9ucyBvciBzdWdnZXN0aW9ucyBmZWVsIGZyZWUgdG8gcmVhY2ggbWUgb3V0IHZpYSBlLW1haWwgPHdpZWN6eW5za2lwYXdlbEBnbWFpbC5jb20+IG9yIFtMaW5rZWRpbl0oaHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL3Bhd2VsLXdpZWN6eW5za2kvKS4gWW91IGNhbiBhbHNvIHZpc2l0IG15IFtHaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9wYXdlbC13aWVjenluc2tpKS4NCg0KYGBge3IgbGlicmFyaWVzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KaWYoIXJlcXVpcmUocGFjbWFuKSkgaW5zdGFsbC5wYWNrYWdlcygncGFjbWFuJykNCnBhY21hbjo6cF9sb2FkKFhNTCwgaHR0ciwgbWFwcywgc3RyaW5nciwgZHBseXIsIGdncGxvdDIpDQpgYGANCg0KVGhpcyBjYXNlIHN0dWR5IG9yaWdpbmF0ZXMgZnJvbSB0aGUgYm9vayAiKkF1dG9tYXRlZCBEYXRhIENvbGxlY3Rpb24gd2l0aCBSOiBBIFByYWN0aWNhbCBHdWlkZSB0byBXZWIgU2NyYXBpbmcgYW5kIFRleHQgTWluaW5nKiIsIHdyaXR0ZW4gYnkgTXVuemVydCwgUnViYmEsIE1lacOfbmVyLCBOeWh1aXMsIHB1Ymxpc2hlZCBieSBXaWxleSBpbiAyMDE1Lg0KDQpBcyBhbiBleGNlcmNpc2UgSSB0cnkgdG8gcmVjcmVhdGUgY2FzZSBzdHVkeSBmcm9tIHRoYXQgYm9vaywgYnV0IHdpdGggdGhlICoqdGlkeXZlcnNlKiogYXBwcm9hY2ggdG8gZGF0YSB3cmFuZ2xpbmcgYW5kICoqZ2dwbG90MioqIGRhdGEgdml6dWFsaXphdGlvbi4gUmVzdWx0cyBtYXkgZGlmZmVyIHNpbmNlIEkgcmVjcmVhdGUgdGhlIHJlc3VsdHMgaW4gRmVicnVhcnkgMjAyMiB3aXRoIHVwZGF0ZWQgZGF0YXNldC4NCg0KTW9yZW92ZXIsIEkgZXhwbG9yZSBmZXcgbW9yZSB0aGluZ3Mgd2hpY2ggaW4gdGhlIGJvb2sgd2VyZSBsZWZ0IGFzIGFuIGV4Y2VyY2lzZSB0byB0aGUgcmVhZGVyLCBpLmUuIGNvdW50cmllcyB3aXRoIG1vc3QgZW5kYW5nZXJlZCBzaXRlcyBhbmQgcGxhY2VzIHdoaWNoIHdlcmUgZW5kYW5nZXJlZCBpbiB0aGUgcGFzdCBidXQgbm93IGFyZW4ndCBlbmRhbmdlcmVkDQoNCg0KIyMjIyBFbmRhbmdlcmVkIHNpdGVzIChyZXN1bHRzIHByZXNlbnRlZCBpbiB0aGUgYm9vaywgYnV0IHJlY3JlYXRlZCB3aXRoIGRwbHlyIGFuZCBnZ3Bsb3QyKQ0KYGBge3IgZW5kYW5nZXJlZFNpdGVzLCB3YXJuaW5nID0gRkFMU0V9DQojIyBHZXQgZGF0YSBmcm9tIHRoZSB3ZWIgIyMNCnVybCA8LSBHRVQoJ2h0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlzdF9vZl9Xb3JsZF9IZXJpdGFnZV9pbl9EYW5nZXInKQ0KdGFibGVzIDwtIHJlYWRIVE1MVGFibGUocmF3VG9DaGFyKHVybCRjb250ZW50KQ0KICAgICAgICAgICAgICAgICAgICAgICAgLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KIyMgU2VsZWN0IDJuZCB0YWJsZSBhbmQgZ2V0IHJpZCBvZiBoZWFkZXJzIGZyb20gMXN0IHJvdyBhbmQgc2VsZWN0IGNvbHVtbnMgIyMNCmRhbmdlclRhYmxlIDwtIHRhYmxlc1tbMl1dICU+JQ0KICAuWy0xLCBjKDEsMyw0LDYsNyldDQoNCiMjIEFkZCBjdXN0b20gaGVhZGVycyAjIw0KY29sbmFtZXMoZGFuZ2VyVGFibGUpIDwtIGMoJ25hbWUnDQogICAgICAgICAgICAgICAgICAgICAgICAgICAsJ2xvY2F0aW9uJw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgLCdjcml0ZXJpYScNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICwneWVhckluc2NyaWJlZCcNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICwneWVhckVuZGFuZ2VyZWQnKQ0KDQojIyBTb21lIGZlYXR1cmVzIHdyYW5nbGluZyAjIw0KZGFuZ2VyVGFibGUgPC0gZGFuZ2VyVGFibGUgJT4lDQogIG11dGF0ZShjcml0ZXJpYSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChjcml0ZXJpYQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCdOYXR1cmFsJykNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsJ05hdHVyYWwnDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLCdDdWx0dXJhbCcpKSAlPiUNCiAgbXV0YXRlKHllYXJJbnNjcmliZWQgPSBhcy5udW1lcmljKHllYXJJbnNjcmliZWQpKSAlPiUNCiAgbXV0YXRlKHllYXJFbmRhbmdlcmVkID0gc3RyX2V4dHJhY3RfYWxsKHllYXJFbmRhbmdlcmVkDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAscGF0dGVybiA9ICdbMC05XXs0feKAkyQnKSAlPiUNCiAgICAgICAgICAgdW5saXN0KCkgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlKHBhdHRlcm4gPSAn4oCTJw0KICAgICAgICAgICAgICAgICAgICAgICAscmVwbGFjZW1lbnQgPSAnJykgJT4lDQogICAgICAgICAgIGFzLm51bWVyaWMoKSkgJT4lDQojIyBFeHRyYWN0aW5nIGNvb3JkaW5hdGVzICMjDQogIG11dGF0ZSh4Q29vcmRzID0gc3RyX2V4dHJhY3QobG9jYXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAscGF0dGVybiA9ICdbO11bIC1dKlswLTldKlsuXSpbMC05XSonKSAlPiUNCiAgICAgICAgICAgc3RyX3N1YihzdGFydCA9IDMNCiAgICAgICAgICAgICAgICAgICAsZW5kID0gLTEpICU+JQ0KICAgICAgICAgICBhcy5udW1lcmljKCkgJT4lDQogICAgICAgICAgIHJvdW5kKDIpKSAlPiUNCiAgbXV0YXRlKHlDb29yZHMgPSBzdHJfZXh0cmFjdChsb2NhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHBhdHRlcm4gPSAnWy9dWyAtXSpbMC05XSpbLl0qWzAtOV0qWztdJykgJT4lDQogICAgICAgICAgIHN0cl9zdWIoc3RhcnQgPSAzDQogICAgICAgICAgICAgICAgICAgLGVuZCA9IC0yKSAlPiUNCiAgICAgICAgICAgYXMubnVtZXJpYygpICU+JQ0KICAgICAgICAgICByb3VuZCgyKSkNCg0KIyMgV29ybGQgbWFwIG9mIGVuZGFuZ2VyZWQgcGxhY2VzICMjDQp3b3JsZCA8LSBtYXBfZGF0YSgnd29ybGQnKQ0KZ2dwbG90KCkgKw0KICBnZW9tX21hcChkYXRhID0gd29ybGQNCiAgICAgICAgICAgLG1hcCA9IHdvcmxkDQogICAgICAgICAgICxhZXMobG9uZw0KICAgICAgICAgICAgICAgICxsYXQNCiAgICAgICAgICAgICAgICAsbWFwX2lkID0gcmVnaW9uKQ0KICAgICAgICAgICAsY29sb3IgPSAnd2hpdGUnDQogICAgICAgICAgICxmaWxsID0gJ2xpZ2h0Z3JheScpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gZGFuZ2VyVGFibGUNCiAgICAgICAgICAgICAsYWVzKHhDb29yZHMNCiAgICAgICAgICAgICAgICAgICx5Q29vcmRzDQogICAgICAgICAgICAgICAgICAsc2hhcGUgPSBjcml0ZXJpYSkNCiAgICAgICAgICAgICAsY29sb3IgPSAncmVkJw0KICAgICAgICAgICAgICxzaXplID0gMi41KSArDQogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE2LDE3KSkgKw0KICBsYWJzKHRpdGxlID0gJ0VuZGFuZ2VyZWQgcGxhY2VzIGluIEZlYnJ1YXJ5IDIwMjInKSArDQogIHRoZW1lX3ZvaWQoKQ0KDQojIyBEaXN0cmlidXRpb24gb2YgeWVhcnMgd2hlbiBwbGFjZXMgYmVjb21lIGVuZGFuZ2VyZWQgIyMNCmdncGxvdChkYW5nZXJUYWJsZSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHllYXJFbmRhbmdlcmVkKQ0KICAgICAgICAgICAgICAgICAsYmlud2lkdGggPSA1DQogICAgICAgICAgICAgICAgICxjb2xvciA9ICdibGFjaycNCiAgICAgICAgICAgICAgICAgLGZpbGwgPSAnd2hpdGUnKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk4MCwgMjAyNSwgYnkgPSA1KSkgKw0KICBsYWJzKHggPSAnJw0KICAgICAgICx5ID0gJycNCiAgICAgICAsdGl0bGUgPSAnWWVhciB3aGVuIHNpdGUgd2FzIHB1dCBvbiB0aGUgbGlzdCBvZiBlbmRhbmdlcmVkIHNpdGVzJykNCg0KIyMgWWVhcnMgaXQgdG9vayB0byBiZWNvbWUgYW4gZW5kYW5nZXJlZCBzaXRlICMjDQpkYW5nZXJUYWJsZSA8LSBkYW5nZXJUYWJsZSAlPiUNCiAgbXV0YXRlKGR1cmF0aW9uID0geWVhckVuZGFuZ2VyZWQgLSB5ZWFySW5zY3JpYmVkKQ0KDQpnZ3Bsb3QoZGFuZ2VyVGFibGUpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBkdXJhdGlvbikNCiAgICAgICAgICAgICAgICAgLGJpbndpZHRoID0gNQ0KICAgICAgICAgICAgICAgICAsY29sb3IgPSAnYmxhY2snDQogICAgICAgICAgICAgICAgICxmaWxsID0gJ3doaXRlJykgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDM1LCBieSA9IDUpKSArDQogIGxhYnMoeCA9ICcnDQogICAgICAgLHkgPSAnJw0KICAgICAgICx0aXRsZSA9ICdZZWFycyBpdCB0b29rIHRvIGJlY29tZSBhbiBlbmRhbmdlcmVkIHNpdGUnKQ0KYGBgDQoNCiMjIyMgV2hpY2ggY291bnRyaWVzIGhhcyB0aGUgbW9zdCBlbmRhbmdlcmVkIHNpdGVzPw0KSm9pbiB3aXRoIG1hcF93b3JsZCBmcm9tIGdncGxvdDIgc2VlbXMgbm90IHdvcmtpbmcgaGVyZSwgc28gSSB1c2UgbWFwLndoZXJlIGZyb20gbWFwcyBwYWNrYWdlLCBidXQgc3RpbGwgd2Ugb2J0YWluIGZldyBOQSdzIHdoaWNoIHByb2JhYmx5IHNob3VsZCBoYXZlIGJlZW4gY2hlY2tlZCBtYW51YWxseSBhdCBnb29nbGUgbWFwcyBvciBvdGhlciBzaW1pbGFyIHNlcnZpY2VzLg0KDQpgYGB7ciBlbmRhbmdlcmVkQ291bnRyaWVzLCB3YXJuaW5nID0gRkFMU0V9DQpkYW5nZXJUYWJsZSA8LSBkYW5nZXJUYWJsZSAlPiUNCiAgbXV0YXRlKGNvdW50cnkgPSBtYXAud2hlcmUoeCA9IHhDb29yZHMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHkgPSB5Q29vcmRzKSkNCg0KY291bnRQZXJDb3VudHJ5IDwtIGRhbmdlclRhYmxlICU+JQ0KICBncm91cF9ieShjb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKQ0KDQpnZ3Bsb3QobmEub21pdChjb3VudFBlckNvdW50cnkpKSArDQogIGdlb21fY29sKGFlcyh4ID0gcmVvcmRlcihjb3VudHJ5LCBjb3VudCkNCiAgICAgICAgICAgICAgICx5ID0gY291bnQpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoeCA9ICcnDQogICAgICAsIHkgPSAnJykNCg0KYGBgDQoNCiMjIyMgU2l0ZXMgZW5kYW5nZXJlZCBpbiB0aGUgcGFzdCBhbmQgY29tcGFyaXNvbiB3aXRoIHRoZSBjdXJyZW50bHkgZW5kYW5nZXJlZCBzaXRlcw0KDQpgYGB7ciBwcmV2aW91c2x5RW5kYW5nZXJlZFBsYWNlcywgd2FybmluZyA9IEZBTFNFfQ0KIyMgU2VsZWN0IDR0aCB0YWJsZSBhbmQgZ2V0IHJpZCBvZiBoZWFkZXJzIGZyb20gMXN0IHJvdyBhbmQgc2VsZWN0IGNvbHVtbnMgIyMNCnByZXZEYW5nZXJUYWJsZSA8LSB0YWJsZXNbWzRdXSAlPiUNCiAgLlstMSwgYygxLDMsNCw2LDcpXQ0KDQojIyBBZGQgY3VzdG9tIGhlYWRlcnMgIyMNCmNvbG5hbWVzKHByZXZEYW5nZXJUYWJsZSkgPC0gY29sbmFtZXMoZGFuZ2VyVGFibGUpWzE6NV0NCg0KIyMgU29tZSBmZWF0dXJlcyB3cmFuZ2xpbmcgIyMNCnByZXZEYW5nZXJUYWJsZSA8LSBwcmV2RGFuZ2VyVGFibGUgJT4lDQogIG11dGF0ZShjcml0ZXJpYSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChjcml0ZXJpYQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCdOYXR1cmFsJykNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAsJ05hdHVyYWwnDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLCdDdWx0dXJhbCcpKSAlPiUNCiAgbXV0YXRlKHllYXJJbnNjcmliZWQgPSBhcy5udW1lcmljKHllYXJJbnNjcmliZWQpKSAlPiUNCiAgbXV0YXRlKGVuZGFuZ2VyU3RhcnRlZCA9IHN0cl9leHRyYWN0X2FsbCh5ZWFyRW5kYW5nZXJlZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxwYXR0ZXJuID0gJ15bMC05XXs0fScpICU+JQ0KICAgICAgICAgICB1bmxpc3QoKSAlPiUNCiAgICAgICAgICAgYXMubnVtZXJpYygpKSAlPiUNCiAgbXV0YXRlKGVuZGFuZ2VyRW5kZWQgPSBzdHJfZXh0cmFjdF9hbGwoeWVhckVuZGFuZ2VyZWQgI3RhayBuYXByYXdkZSBlbmRhbmdlciBlbmRlZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHBhdHRlcm4gPSAnWzAtOV17NH0kJykgJT4lDQogICAgICAgICAgIHVubGlzdCgpICU+JQ0KICAgICAgICAgICBhcy5udW1lcmljKCkpICU+JQ0KIyMgRXh0cmFjdGluZyBjb29yZGluYXRlcyBhbmQgY291bnRyaWVzIyMNCiAgbXV0YXRlKHhDb29yZHMgPSBzdHJfZXh0cmFjdChsb2NhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxwYXR0ZXJuID0gJ1s7XVsgLV0qWzAtOV0qWy5dKlswLTldKicpICU+JQ0KICAgICAgICAgICBzdHJfc3ViKHN0YXJ0ID0gMw0KICAgICAgICAgICAgICAgICAgICxlbmQgPSAtMSkgJT4lDQogICAgICAgICAgIGFzLm51bWVyaWMoKSAlPiUNCiAgICAgICAgICAgcm91bmQoMikpICU+JQ0KICBtdXRhdGUoeUNvb3JkcyA9IHN0cl9leHRyYWN0KGxvY2F0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHBhdHRlcm4gPSAnWy9dWyAtXSpbMC05XSpbLl0qWzAtOV0qWztdJykgJT4lDQogICAgICAgICAgIHN0cl9zdWIoc3RhcnQgPSAzDQogICAgICAgICAgICAgICAgICAgLGVuZCA9IC0yKSAlPiUNCiAgICAgICAgICAgYXMubnVtZXJpYygpICU+JQ0KICAgICAgICAgICByb3VuZCgyKSkgJT4lDQogIG11dGF0ZShjb3VudHJ5ID0gbWFwLndoZXJlKHggPSB4Q29vcmRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICx5ID0geUNvb3JkcykpDQoNCiMjIEhvdyBtdWNoIHNpdGVzIGRpc2FwcGVhcmVkIGZyb20gdGhlIGxpc3QgIyMNCmdncGxvdChwcmV2RGFuZ2VyVGFibGUpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGVuZGFuZ2VyRW5kZWQpDQogICAgICAgICAgICAgICAgICxiaW53aWR0aCA9IDUNCiAgICAgICAgICAgICAgICAgLGNvbG9yID0gJ2JsYWNrJw0KICAgICAgICAgICAgICAgICAsZmlsbCA9ICd3aGl0ZScpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTgwLCAyMDI1LCBieSA9IDUpKSArDQogIGxhYnMoeCA9ICcnDQogICAgICAgLHkgPSAnJw0KICAgICAgICx0aXRsZSA9ICdFZmZlY3RpdmVuZXMgb2YgV29ybGQgSGVyaXRhZ2UgQ29tbWl0dGVlJykNCg0KIyMgQXZlcmFnZSBlbmRhZ2VybWVudCB0aW1lICMjDQpwcmV2RGFuZ2VyVGFibGUgPC0gcHJldkRhbmdlclRhYmxlICU+JQ0KICBtdXRhdGUoZW5kYW5nZXJEdXJhdGlvbiA9IGVuZGFuZ2VyRW5kZWQgLSBlbmRhbmdlclN0YXJ0ZWQpDQoNCmdncGxvdChwcmV2RGFuZ2VyVGFibGUpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGVuZGFuZ2VyRHVyYXRpb24pDQogICAgICAgICAgICAgICAgICxiaW53aWR0aCA9IDUNCiAgICAgICAgICAgICAgICAgLGNvbG9yID0gJ2JsYWNrJw0KICAgICAgICAgICAgICAgICAsZmlsbCA9ICd3aGl0ZScpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyNSwgYnkgPSA1KSkgKw0KICBsYWJzKHggPSAnJw0KICAgICAgICx5ID0gJycNCiAgICAgICAsdGl0bGUgPSAnRW5kYW5nZXIgZHVyYXRpb24nKSArDQogIGFubm90YXRlKCd0ZXh0Jw0KICAgICAgICAgICAseCA9IDIwDQogICAgICAgICAgICx5ID0gMTANCiAgICAgICAgICAgLGxhYmVsID0gcGFzdGUwKCdBdmVyYWdlIGR1cmF0aW9uIFxuJw0KICAgICAgICAgICAgICxyb3VuZChtZWFuKHByZXZEYW5nZXJUYWJsZSRlbmRhbmdlckR1cmF0aW9uKSwgMikNCiAgICAgICAgICAgICAsJyB5ZWFycycpDQogICAgICAgICAgICxzaXplID0gNikNCg0KIyMjIyBDT01QQVJJU09OIE9GIENVUlJFTlQgQU5EIFBSRVZJT1VTIEVOREFOR0VSRUQgUExBQ0VTICMjIyMNCiMjIE1lcmdlIHRhYmxlcyAjIw0KYWxsUGxhY2VzIDwtIGJpbmRfcm93cygNCiAgZGFuZ2VyVGFibGUgJT4lDQogICAgbXV0YXRlKGVuZGFuZ2VyZWQgPSAnWWVzJykNCiAgLHByZXZEYW5nZXJUYWJsZSAlPiUNCiAgICBtdXRhdGUoZW5kYW5nZXJlZCA9ICdObycpICU+JQ0KICAgIHNlbGVjdCgteWVhckVuZGFuZ2VyZWQpDQopDQoNCiMjIFdvcmxkIG1hcCAjJA0KZ2dwbG90KCkgKw0KICBnZW9tX21hcChkYXRhID0gd29ybGQNCiAgICAgICAgICAgLG1hcCA9IHdvcmxkDQogICAgICAgICAgICxhZXMobG9uZw0KICAgICAgICAgICAgICAgICxsYXQNCiAgICAgICAgICAgICAgICAsbWFwX2lkID0gcmVnaW9uKQ0KICAgICAgICAgICAsY29sb3IgPSAnd2hpdGUnDQogICAgICAgICAgICxmaWxsID0gJ2xpZ2h0Z3JheScpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gYWxsUGxhY2VzDQogICAgICAgICAgICAgLGFlcyh4Q29vcmRzDQogICAgICAgICAgICAgICAgICAseUNvb3Jkcw0KICAgICAgICAgICAgICAgICAgLHNoYXBlID0gY3JpdGVyaWENCiAgICAgICAgICAgICAgICAgICxjb2xvciA9IGVuZGFuZ2VyZWQpDQogICAgICAgICAgICAgLHNpemUgPSAyLjUpICsNCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTcsIDE5KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnZGFya2dyZWVuJywgJ3JlZCcpKSArDQogIGxhYnModGl0bGUgPSAnRW5kYW5nZXJlZCBhbmQgcHJldmlvdXNseSBlbmRhbmdlcmVkIHBsYWNlcyBpbiBGZWJydWFyeSAyMDIyJykgKw0KICB0aGVtZV92b2lkKCkNCmBgYA==