# Import required libraries
library(readr)
library(dplyr)
library(ggvis)
library(fuzzyjoin)
library(janitor)

Import CSV files

# Import raw CSV files
country_list <- read_delim('/Users/todddequincey/Google Drive/Education & Learning/Data Science/USQ/Semester 2 2018/CSC8002 Big Data Management/Project/Datasets/cleaned_datasets/country_cleaned.csv', delim=",")
co2 <-  read_delim('/Users/todddequincey/Google Drive/Education & Learning/Data Science/USQ/Semester 2 2018/CSC8002 Big Data Management/Project/Datasets/uncleaned_datasets/annual-co-emissions-per-country.csv', delim=",")

Fuzzy Match

Joining of the CO2 and country_list datasets based on the country name attribute in both datasets.
As the country names may not 100% match, a fuzzy match of the country names and manual mapping of any unmatched names is completed to allow for the two datasets to be used together.

# Set country names to lower in both files to avoid case sensitivity
co2$country_name <- tolower(co2$Entity)
country_list$country_name <- tolower(country_list$`COUNTRY NAME`)
# Function to fuzzy match country names
unmatched_country_names <- function(df1, df2, column_name){
  df1_col <- unique(select(df1,column_name))
  df2_col <- unique(select(df2,column_name))
  
  name_matches <- stringdist_left_join(x = df1_col, y= df2_col, max_dist=0)
  
  # Return all positive matches from fuzzy join which are NOT exact matches
  positive_name_matches <- name_matches %>% filter(complete.cases(.))
  filter(positive_name_matches, !(1 == 2))
  unmatched <- name_matches %>% filter_all(any_vars(is.na(.)))
  return(unmatched)
}
# Return a dataframe of country names which do not match between country_list and co2 datasets
unmatched <- unmatched_country_names(co2,country_list, 'country_name')
Joining by: "country_name"
unmatched

43 rows with unmatched names.
Majority of the unmatched countries are either regions of the world (e.g. south america), countries with island, south, north or saint in their name or other records (e.g. world, annex b), which may have been abbreviated in the country_list dataset (e.g. north might be nth) or are irrelvant for our purposes.
Also several ‘annex b’ types, which are unknown.
All of these non-country records are to be removed.

# Non-countries in unmatched
non_countries <- c('africa', 'asia', 'central america', 'eu28', 'europe', 'kp annex b', 'middle east', 'non kp annex b', 'non-oecd', 'north america', 'oecd','oceania', 'statistical differences', 'world')
# Create a subset of the data excluding the non-country records
co2 <- subset(co2, !(country_name %in% non_countries))
# Group by country name and review for further non-country records
co2 %>%
  group_by(country_name) %>%
  summarise()
unmatched2 <- unmatched_country_names(co2,country_list, 'country_name')
Joining by: "country_name"
unmatched2

After re-running the match on the cleaned data, there are now 29 countries which do not have matched.
Options to match them could include regex or more detailed, prescriptive fuzzy matching, however given there are only 29 unmatched countries, resolved to manual match them.

# List of unmatched countries which have a corresponding record in the country_list dataset
unmatched_countries = c('czechoslovakia', 'democratic republic of congo', 'faeroe islands', 'falkland islands', 'gambia', 'micronesia (country)', 'north korea', 'palau', 'reunion', 'saint helena', 'saint kitts and nevis', 'saint lucia', 'saint pierre and miquelon', 'saint vincent and the grenadines', 'sint maarten (dutch part)', 'south korea', 'south sudan', 'yugoslavia')
# A list manually mapping the unmatched countries to the country_list dataset
manually_matched <- c('czech republic', 'congo', 'faroe islands', 'falkland islands (islas malvinas)', 'gambia  the', 'micronesia, federated states of', 'korea, north', 'palau - trust territory of the pacific islands', 'reunion and associated islands', 'st. helena', 'st. kitts and nevis', 'st. lucia', 'st. pierre and miquelon','st. lucia and st. vincent', 'st. marteen, st. eustatius, and saba', 'korea, south', 'sudan', 'yugoslavia (& former territory)')
# Map and update the country names in co2 dataset using the manually_matched names
count <- 1
for (country in unmatched_countries) {
  co2 <- co2 %>%
    mutate(country_name = ifelse (country_name == country, country_name <- manually_matched[count], co2$country_name))
    count <- count + 1
}
# Review the remaining unmatched countries
unmatched_round2 <- unmatched_country_names(co2,country_list, 'country_name')
Joining by: "country_name"
unmatched_round2

Manually checked and these countries do not exist in the country_list dataset.
Resolved to remove these from the co2 dataset.

# Drop countries from co2 data which do not have a corresponding record in the country_list dataset
co2 <- subset(co2, !(country_name %in% unmatched_round2$country_name.x))
# Combine the co2 emissions for countries with the same name but different codes
co2 <- co2 %>%
  group_by(country_name, Year) %>%
  summarise(co2 = sum(`Annual COâ‚‚ emissions (Global Carbon Project (2017)) (million tonnes)`))

General Data Cleaning

Remove any empty rows and columns
# Remove rows and columns with totally empty values 
remove_empty(co2, 'rows')
remove_empty(co2, 'cols')

Results
No empty rows or columns found. Therefore no changes to the df

Check for missing values
# Check for missing values / nulls on key 
anyNA(co2)
[1] FALSE

Results
No missing values identified.

Check for negative CO2 emission values
# Return the minimum value for CO2 emissions column
min(co2[,3])
[1] 0

Results
Minimum value in co2_emissions column is 0. Therefore, no negative values.

Check year range is correct
# Return the minimum year value
min_year <- min(co2[,2])
max_year <-  max(co2[,2])
min_year
[1] 1751
max_year
[1] 2016

Results
Minimum value in the year column is 1751, with a maximum of 2016.
Values are within expected range.

Export results to CSV

# Write cleaned data back to csv for import into MySQL
write_csv(co2, '/Users/todddequincey/Google Drive/Education & Learning/Data Science/USQ/Semester 2 2018/CSC8002 Big Data Management/Project/Datasets/cleaned_datasets/co2_cleaned.csv')
LS0tCnRpdGxlOiAiQ08yIERhdGEgQ2xlYW5pbmciCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3J9CiMgSW1wb3J0IHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdndmlzKQpsaWJyYXJ5KGZ1enp5am9pbikKbGlicmFyeShqYW5pdG9yKQpgYGAKCgojIyBJbXBvcnQgQ1NWIGZpbGVzCmBgYHtyfQojIEltcG9ydCByYXcgQ1NWIGZpbGVzCmNvdW50cnlfbGlzdCA8LSByZWFkX2RlbGltKCcvVXNlcnMvdG9kZGRlcXVpbmNleS9Hb29nbGUgRHJpdmUvRWR1Y2F0aW9uICYgTGVhcm5pbmcvRGF0YSBTY2llbmNlL1VTUS9TZW1lc3RlciAyIDIwMTgvQ1NDODAwMiBCaWcgRGF0YSBNYW5hZ2VtZW50L1Byb2plY3QvRGF0YXNldHMvY2xlYW5lZF9kYXRhc2V0cy9jb3VudHJ5X2NsZWFuZWQuY3N2JywgZGVsaW09IiwiKQpjbzIgPC0gIHJlYWRfZGVsaW0oJy9Vc2Vycy90b2RkZGVxdWluY2V5L0dvb2dsZSBEcml2ZS9FZHVjYXRpb24gJiBMZWFybmluZy9EYXRhIFNjaWVuY2UvVVNRL1NlbWVzdGVyIDIgMjAxOC9DU0M4MDAyIEJpZyBEYXRhIE1hbmFnZW1lbnQvUHJvamVjdC9EYXRhc2V0cy91bmNsZWFuZWRfZGF0YXNldHMvYW5udWFsLWNvLWVtaXNzaW9ucy1wZXItY291bnRyeS5jc3YnLCBkZWxpbT0iLCIpCmBgYAoKIyMgRnV6enkgTWF0Y2gKSm9pbmluZyBvZiB0aGUgQ08yIGFuZCBjb3VudHJ5X2xpc3QgZGF0YXNldHMgYmFzZWQgb24gdGhlIGNvdW50cnkgbmFtZSBhdHRyaWJ1dGUgaW4gYm90aCBkYXRhc2V0cy4gIApBcyB0aGUgY291bnRyeSBuYW1lcyBtYXkgbm90IDEwMCUgbWF0Y2gsIGEgZnV6enkgbWF0Y2ggb2YgdGhlIGNvdW50cnkgbmFtZXMgYW5kIG1hbnVhbCBtYXBwaW5nIG9mIGFueSB1bm1hdGNoZWQgbmFtZXMgaXMgY29tcGxldGVkIHRvIGFsbG93IGZvciB0aGUgdHdvIGRhdGFzZXRzIHRvIGJlIHVzZWQgdG9nZXRoZXIuICAgCmBgYHtyfQojIFNldCBjb3VudHJ5IG5hbWVzIHRvIGxvd2VyIGluIGJvdGggZmlsZXMgdG8gYXZvaWQgY2FzZSBzZW5zaXRpdml0eQpjbzIkY291bnRyeV9uYW1lIDwtIHRvbG93ZXIoY28yJEVudGl0eSkKY291bnRyeV9saXN0JGNvdW50cnlfbmFtZSA8LSB0b2xvd2VyKGNvdW50cnlfbGlzdCRgQ09VTlRSWSBOQU1FYCkKYGBgCgoKYGBge3J9CiMgRnVuY3Rpb24gdG8gZnV6enkgbWF0Y2ggY291bnRyeSBuYW1lcwp1bm1hdGNoZWRfY291bnRyeV9uYW1lcyA8LSBmdW5jdGlvbihkZjEsIGRmMiwgY29sdW1uX25hbWUpewogIGRmMV9jb2wgPC0gdW5pcXVlKHNlbGVjdChkZjEsY29sdW1uX25hbWUpKQogIGRmMl9jb2wgPC0gdW5pcXVlKHNlbGVjdChkZjIsY29sdW1uX25hbWUpKQogIAogIG5hbWVfbWF0Y2hlcyA8LSBzdHJpbmdkaXN0X2xlZnRfam9pbih4ID0gZGYxX2NvbCwgeT0gZGYyX2NvbCwgbWF4X2Rpc3Q9MCkKICAKICAjIFJldHVybiBhbGwgcG9zaXRpdmUgbWF0Y2hlcyBmcm9tIGZ1enp5IGpvaW4gd2hpY2ggYXJlIE5PVCBleGFjdCBtYXRjaGVzCiAgcG9zaXRpdmVfbmFtZV9tYXRjaGVzIDwtIG5hbWVfbWF0Y2hlcyAlPiUgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pKQogIGZpbHRlcihwb3NpdGl2ZV9uYW1lX21hdGNoZXMsICEoMSA9PSAyKSkKICB1bm1hdGNoZWQgPC0gbmFtZV9tYXRjaGVzICU+JSBmaWx0ZXJfYWxsKGFueV92YXJzKGlzLm5hKC4pKSkKICByZXR1cm4odW5tYXRjaGVkKQp9CmBgYAoKYGBge3J9CiMgUmV0dXJuIGEgZGF0YWZyYW1lIG9mIGNvdW50cnkgbmFtZXMgd2hpY2ggZG8gbm90IG1hdGNoIGJldHdlZW4gY291bnRyeV9saXN0IGFuZCBjbzIgZGF0YXNldHMKdW5tYXRjaGVkIDwtIHVubWF0Y2hlZF9jb3VudHJ5X25hbWVzKGNvMixjb3VudHJ5X2xpc3QsICdjb3VudHJ5X25hbWUnKQp1bm1hdGNoZWQKYGBgCmByIGFzLmludGVnZXIoY291bnQodW5pcXVlKHVubWF0Y2hlZCkpKWAgcm93cyB3aXRoIHVubWF0Y2hlZCBuYW1lcy4gIApNYWpvcml0eSBvZiB0aGUgdW5tYXRjaGVkIGNvdW50cmllcyBhcmUgZWl0aGVyIHJlZ2lvbnMgb2YgdGhlIHdvcmxkIChlLmcuIHNvdXRoIGFtZXJpY2EpLCBjb3VudHJpZXMgd2l0aCBpc2xhbmQsIHNvdXRoLCBub3J0aCBvciBzYWludCBpbiB0aGVpciBuYW1lIG9yIG90aGVyIHJlY29yZHMgKGUuZy4gd29ybGQsIGFubmV4IGIpLCB3aGljaCBtYXkgaGF2ZSBiZWVuIGFiYnJldmlhdGVkIGluIHRoZSBjb3VudHJ5X2xpc3QgZGF0YXNldCAoZS5nLiBub3J0aCBtaWdodCBiZSBudGgpIG9yIGFyZSBpcnJlbHZhbnQgZm9yIG91ciBwdXJwb3Nlcy4gIApBbHNvIHNldmVyYWwgJ2FubmV4IGInIHR5cGVzLCB3aGljaCBhcmUgdW5rbm93bi4gIApBbGwgb2YgdGhlc2Ugbm9uLWNvdW50cnkgcmVjb3JkcyBhcmUgdG8gYmUgcmVtb3ZlZC4gCgpgYGB7cn0KIyBOb24tY291bnRyaWVzIGluIHVubWF0Y2hlZApub25fY291bnRyaWVzIDwtIGMoJ2FmcmljYScsICdhc2lhJywgJ2NlbnRyYWwgYW1lcmljYScsICdldTI4JywgJ2V1cm9wZScsICdrcCBhbm5leCBiJywgJ21pZGRsZSBlYXN0JywgJ25vbiBrcCBhbm5leCBiJywgJ25vbi1vZWNkJywgJ25vcnRoIGFtZXJpY2EnLCAnb2VjZCcsJ29jZWFuaWEnLCAnc3RhdGlzdGljYWwgZGlmZmVyZW5jZXMnLCAnd29ybGQnKQoKIyBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgZXhjbHVkaW5nIHRoZSBub24tY291bnRyeSByZWNvcmRzCmNvMiA8LSBzdWJzZXQoY28yLCAhKGNvdW50cnlfbmFtZSAlaW4lIG5vbl9jb3VudHJpZXMpKQoKIyBHcm91cCBieSBjb3VudHJ5IG5hbWUgYW5kIHJldmlldyBmb3IgZnVydGhlciBub24tY291bnRyeSByZWNvcmRzCmNvMiAlPiUKICBncm91cF9ieShjb3VudHJ5X25hbWUpICU+JQogIHN1bW1hcmlzZSgpCmBgYAoKYGBge3J9CnVubWF0Y2hlZDIgPC0gdW5tYXRjaGVkX2NvdW50cnlfbmFtZXMoY28yLGNvdW50cnlfbGlzdCwgJ2NvdW50cnlfbmFtZScpCnVubWF0Y2hlZDIKYGBgCkFmdGVyIHJlLXJ1bm5pbmcgdGhlIG1hdGNoIG9uIHRoZSBjbGVhbmVkIGRhdGEsIHRoZXJlIGFyZSBub3cgYHIgYXMuaW50ZWdlcihjb3VudCh1bmlxdWUodW5tYXRjaGVkMikpKWAgY291bnRyaWVzIHdoaWNoIGRvIG5vdCBoYXZlIG1hdGNoZWQuICAKT3B0aW9ucyB0byBtYXRjaCB0aGVtIGNvdWxkIGluY2x1ZGUgcmVnZXggb3IgbW9yZSBkZXRhaWxlZCwgcHJlc2NyaXB0aXZlIGZ1enp5IG1hdGNoaW5nLCBob3dldmVyIGdpdmVuIHRoZXJlIGFyZSBvbmx5IGByIGFzLmludGVnZXIoY291bnQodW5pcXVlKHVubWF0Y2hlZDIpKSlgIHVubWF0Y2hlZCBjb3VudHJpZXMsIHJlc29sdmVkIHRvIG1hbnVhbCBtYXRjaCB0aGVtLiAgCgoKYGBge3J9CiMgTGlzdCBvZiB1bm1hdGNoZWQgY291bnRyaWVzIHdoaWNoIGhhdmUgYSBjb3JyZXNwb25kaW5nIHJlY29yZCBpbiB0aGUgY291bnRyeV9saXN0IGRhdGFzZXQKdW5tYXRjaGVkX2NvdW50cmllcyA9IGMoJ2N6ZWNob3Nsb3Zha2lhJywgJ2RlbW9jcmF0aWMgcmVwdWJsaWMgb2YgY29uZ28nLCAnZmFlcm9lIGlzbGFuZHMnLCAnZmFsa2xhbmQgaXNsYW5kcycsICdnYW1iaWEnLCAnbWljcm9uZXNpYSAoY291bnRyeSknLCAnbm9ydGgga29yZWEnLCAncGFsYXUnLCAncmV1bmlvbicsICdzYWludCBoZWxlbmEnLCAnc2FpbnQga2l0dHMgYW5kIG5ldmlzJywgJ3NhaW50IGx1Y2lhJywgJ3NhaW50IHBpZXJyZSBhbmQgbWlxdWVsb24nLCAnc2FpbnQgdmluY2VudCBhbmQgdGhlIGdyZW5hZGluZXMnLCAnc2ludCBtYWFydGVuIChkdXRjaCBwYXJ0KScsICdzb3V0aCBrb3JlYScsICdzb3V0aCBzdWRhbicsICd5dWdvc2xhdmlhJykKCiMgQSBsaXN0IG1hbnVhbGx5IG1hcHBpbmcgdGhlIHVubWF0Y2hlZCBjb3VudHJpZXMgdG8gdGhlIGNvdW50cnlfbGlzdCBkYXRhc2V0Cm1hbnVhbGx5X21hdGNoZWQgPC0gYygnY3plY2ggcmVwdWJsaWMnLCAnY29uZ28nLCAnZmFyb2UgaXNsYW5kcycsICdmYWxrbGFuZCBpc2xhbmRzIChpc2xhcyBtYWx2aW5hcyknLCAnZ2FtYmlhICB0aGUnLCAnbWljcm9uZXNpYSwgZmVkZXJhdGVkIHN0YXRlcyBvZicsICdrb3JlYSwgbm9ydGgnLCAncGFsYXUgLSB0cnVzdCB0ZXJyaXRvcnkgb2YgdGhlIHBhY2lmaWMgaXNsYW5kcycsICdyZXVuaW9uIGFuZCBhc3NvY2lhdGVkIGlzbGFuZHMnLCAnc3QuIGhlbGVuYScsICdzdC4ga2l0dHMgYW5kIG5ldmlzJywgJ3N0LiBsdWNpYScsICdzdC4gcGllcnJlIGFuZCBtaXF1ZWxvbicsJ3N0LiBsdWNpYSBhbmQgc3QuIHZpbmNlbnQnLCAnc3QuIG1hcnRlZW4sIHN0LiBldXN0YXRpdXMsIGFuZCBzYWJhJywgJ2tvcmVhLCBzb3V0aCcsICdzdWRhbicsICd5dWdvc2xhdmlhICgmIGZvcm1lciB0ZXJyaXRvcnkpJykKCgojIE1hcCBhbmQgdXBkYXRlIHRoZSBjb3VudHJ5IG5hbWVzIGluIGNvMiBkYXRhc2V0IHVzaW5nIHRoZSBtYW51YWxseV9tYXRjaGVkIG5hbWVzCmNvdW50IDwtIDEKZm9yIChjb3VudHJ5IGluIHVubWF0Y2hlZF9jb3VudHJpZXMpIHsKICBjbzIgPC0gY28yICU+JQogICAgbXV0YXRlKGNvdW50cnlfbmFtZSA9IGlmZWxzZSAoY291bnRyeV9uYW1lID09IGNvdW50cnksIGNvdW50cnlfbmFtZSA8LSBtYW51YWxseV9tYXRjaGVkW2NvdW50XSwgY28yJGNvdW50cnlfbmFtZSkpCiAgICBjb3VudCA8LSBjb3VudCArIDEKfQpgYGAKCgpgYGB7cn0KIyBSZXZpZXcgdGhlIHJlbWFpbmluZyB1bm1hdGNoZWQgY291bnRyaWVzCnVubWF0Y2hlZF9yb3VuZDIgPC0gdW5tYXRjaGVkX2NvdW50cnlfbmFtZXMoY28yLGNvdW50cnlfbGlzdCwgJ2NvdW50cnlfbmFtZScpCnVubWF0Y2hlZF9yb3VuZDIKYGBgCk1hbnVhbGx5IGNoZWNrZWQgYW5kIHRoZXNlIGNvdW50cmllcyBkbyBub3QgZXhpc3QgaW4gdGhlIGNvdW50cnlfbGlzdCBkYXRhc2V0LiAgClJlc29sdmVkIHRvIHJlbW92ZSB0aGVzZSBmcm9tIHRoZSBjbzIgZGF0YXNldC4gIAoKCmBgYHtyfQojIERyb3AgY291bnRyaWVzIGZyb20gY28yIGRhdGEgd2hpY2ggZG8gbm90IGhhdmUgYSBjb3JyZXNwb25kaW5nIHJlY29yZCBpbiB0aGUgY291bnRyeV9saXN0IGRhdGFzZXQKY28yIDwtIHN1YnNldChjbzIsICEoY291bnRyeV9uYW1lICVpbiUgdW5tYXRjaGVkX3JvdW5kMiRjb3VudHJ5X25hbWUueCkpCmBgYAoKCgpgYGB7cn0KIyBDb21iaW5lIHRoZSBjbzIgZW1pc3Npb25zIGZvciBjb3VudHJpZXMgd2l0aCB0aGUgc2FtZSBuYW1lIGJ1dCBkaWZmZXJlbnQgY29kZXMKY28yIDwtIGNvMiAlPiUKICBncm91cF9ieShjb3VudHJ5X25hbWUsIFllYXIpICU+JQogIHN1bW1hcmlzZShjbzIgPSBzdW0oYEFubnVhbCBDT+KCgiBlbWlzc2lvbnMgKEdsb2JhbCBDYXJib24gUHJvamVjdCAoMjAxNykpIChtaWxsaW9uIHRvbm5lcylgKSkKYGBgCgoKIyMgR2VuZXJhbCBEYXRhIENsZWFuaW5nCiMjIyMjIFJlbW92ZSBhbnkgZW1wdHkgcm93cyBhbmQgY29sdW1ucwpgYGB7cn0KIyBSZW1vdmUgcm93cyBhbmQgY29sdW1ucyB3aXRoIHRvdGFsbHkgZW1wdHkgdmFsdWVzIApyZW1vdmVfZW1wdHkoY28yLCAncm93cycpCnJlbW92ZV9lbXB0eShjbzIsICdjb2xzJykKYGBgCioqUmVzdWx0cyoqICAKTm8gZW1wdHkgcm93cyBvciBjb2x1bW5zIGZvdW5kLiBUaGVyZWZvcmUgbm8gY2hhbmdlcyB0byB0aGUgZGYKCgojIyMjIyBDaGVjayBmb3IgbWlzc2luZyB2YWx1ZXMKYGBge3J9CiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzIC8gbnVsbHMgb24ga2V5IAphbnlOQShjbzIpCmBgYAoqKlJlc3VsdHMqKiAgCk5vIG1pc3NpbmcgdmFsdWVzIGlkZW50aWZpZWQuCgoKIyMjIyMgQ2hlY2sgZm9yIG5lZ2F0aXZlIENPMiBlbWlzc2lvbiB2YWx1ZXMKYGBge3J9CiMgUmV0dXJuIHRoZSBtaW5pbXVtIHZhbHVlIGZvciBDTzIgZW1pc3Npb25zIGNvbHVtbgptaW4oY28yWywzXSkKYGBgCioqUmVzdWx0cyoqICAKTWluaW11bSB2YWx1ZSBpbiBjbzJfZW1pc3Npb25zIGNvbHVtbiBpcyBgciBtaW4oY28yWywzXSlgLiBUaGVyZWZvcmUsIG5vIG5lZ2F0aXZlIHZhbHVlcy4KCgojIyMjIyBDaGVjayB5ZWFyIHJhbmdlIGlzIGNvcnJlY3QKYGBge3J9CiMgUmV0dXJuIHRoZSBtaW5pbXVtIHllYXIgdmFsdWUKbWluX3llYXIgPC0gbWluKGNvMlssMl0pCm1heF95ZWFyIDwtICBtYXgoY28yWywyXSkKbWluX3llYXIKbWF4X3llYXIKYGBgCioqUmVzdWx0cyoqICAKTWluaW11bSB2YWx1ZSBpbiB0aGUgeWVhciBjb2x1bW4gaXMgYHIgbWluX3llYXJgLCB3aXRoIGEgbWF4aW11bSBvZiBgciBtYXhfeWVhcmAuICAKVmFsdWVzIGFyZSB3aXRoaW4gZXhwZWN0ZWQgcmFuZ2UuCgoKIyMgRXhwb3J0IHJlc3VsdHMgdG8gQ1NWCmBgYHtyfQojIFdyaXRlIGNsZWFuZWQgZGF0YSBiYWNrIHRvIGNzdiBmb3IgaW1wb3J0IGludG8gTXlTUUwKd3JpdGVfY3N2KGNvMiwgJy9Vc2Vycy90b2RkZGVxdWluY2V5L0dvb2dsZSBEcml2ZS9FZHVjYXRpb24gJiBMZWFybmluZy9EYXRhIFNjaWVuY2UvVVNRL1NlbWVzdGVyIDIgMjAxOC9DU0M4MDAyIEJpZyBEYXRhIE1hbmFnZW1lbnQvUHJvamVjdC9EYXRhc2V0cy9jbGVhbmVkX2RhdGFzZXRzL2NvMl9jbGVhbmVkLmNzdicpCmBgYAoKCgoK