Setting up the library


  • Packages:
     ggplot2        dplyr        psych summarytools       readxl    lubridate      stringr 
        TRUE         TRUE         TRUE         TRUE         TRUE         TRUE         TRUE 


Data Exploration

Exploration of the raw data - base and dplyr


  • Data entry:
bmi_1 = read_excel("bmi.xlsx", sheet = 2)


  • Check the class of bmi:
class(bmi_1)
[1] "tbl_df"     "tbl"        "data.frame"


  • Check the dimensions of bmi:
dim(bmi_1)
[1] 199   2


  • View the column names of bmi:
colnames(bmi_1)
[1] "Country"  "BMI_1980"


  • Structure of the data:
str(bmi_1)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   199 obs. of  2 variables:
 $ Country : chr  "Afghanistan" "Albania" "Algeria" "Andorra" ...
 $ BMI_1980: num  20.4 25.2 23.7 25.7 20.1 ...


  • Glimpse:
# library(dplyr), install.packages(“dplyr“)
glimpse(bmi_1)
Observations: 199
Variables: 2
$ Country  <chr> "Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Antigua and Barbuda", "Argentina", "Armenia"...
$ BMI_1980 <dbl> 20.44348, 25.17427, 23.67764, 25.67324, 20.06763, 24.22235, 23.84436, 25.77727, 23.63058, 23.90340, 26....


  • Summary:
summary(bmi_1)
   Country             BMI_1980    
 Length:199         Min.   :18.47  
 Class :character   1st Qu.:21.38  
 Mode  :character   Median :23.98  
                    Mean   :23.55  
                    3rd Qu.:25.40  
                    Max.   :28.28  


  • First 10 rows:
head(bmi_1, n = 10)


  • Last 10 rows:
tail(bmi_1, n = 10)


Exploration of the raw data - psych


  • Load psych:
# library(psych), install.packages("psych")


  • Check the structure of bmi, the psych way:
describe(bmi_1)


  • Check the structure of bmi, the psych way - by group:
bmi_2 <- bmi_1 %>%
         group_by(Country) %>%
         mutate(Health = if_else(BMI_1980 <= 18.5 | BMI_1980 >= 24.9, 
                                 "Unhealthy", "Normal"))
describe.by(bmi_2$BMI_1980, group = factor(bmi_2$Health))

 Descriptive statistics by group 
group: Normal
--------------------------------------------------------------------------------------------- 
group: Unhealthy


Exploration of the raw data - summarytools


  • Load summarytools:
# library(summarytools), install.packages("summarytools")


  • Data:
Manpower <- read.csv('Manpower.csv')


  • Check the structure of bmi, the summarytools way:
view(dfSummary(Manpower))

Introduction to Data Wrangling


  • Data:
Infrastructure <- read.csv2("Infrastructure.csv")


  • Preview Infrastructure with str():
str(Infrastructure)
'data.frame':   133 obs. of  7 variables:
 $ Country         : Factor w/ 133 levels "Afghanistan",..: 126 97 26 49 40 125 56 120 43 35 ...
 $ ISO3            : Factor w/ 133 levels "AFG","ALB","ALG",..: 127 97 25 49 40 124 57 121 43 35 ...
 $ Rank            : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Ports           : int  24 7 15 7 14 14 10 9 13 7 ...
 $ Roadway_Coverage: int  6586610 982000 3860800 3320410 951200 394428 1210251 352046 644480 65050 ...
 $ Railway_Coverage: int  224792 87157 86000 63974 29640 16454 27182 8699 41981 5083 ...
 $ Airports        : int  13513 1218 507 346 464 460 175 98 539 83 ...


  • Coerce Country to character:
Infrastructure$Country <- as.character(Infrastructure$Country)


  • Coerce Rank to factor:
Infrastructure$Rank <- as.character(Infrastructure$Rank)


  • Look at Infrastructure once more with str():
str(Infrastructure)
'data.frame':   133 obs. of  7 variables:
 $ Country         : chr  "United States" "Russia" "China" "India" ...
 $ ISO3            : Factor w/ 133 levels "AFG","ALB","ALG",..: 127 97 25 49 40 124 57 121 43 35 ...
 $ Rank            : chr  "1" "2" "3" "4" ...
 $ Ports           : int  24 7 15 7 14 14 10 9 13 7 ...
 $ Roadway_Coverage: int  6586610 982000 3860800 3320410 951200 394428 1210251 352046 644480 65050 ...
 $ Railway_Coverage: int  224792 87157 86000 63974 29640 16454 27182 8699 41981 5083 ...
 $ Airports        : int  13513 1218 507 346 464 460 175 98 539 83 ...


Strings


  • Load the stringr package:
# library("stringr") install.packages("stringr")


  • Trim all leading and trailing whitespace:
name = c(" Filip ", "Nick ", " Jonathan")
str_trim(name)
[1] "Filip"    "Nick"     "Jonathan"


  • Pad these strings with leading zeros:
pad = c("23485W", "8823453Q", "994Z")
str_pad(pad, width = 9, side = "left", pad = "0")
[1] "00023485W" "08823453Q" "00000994Z"


  • Print state abbreviations:
head(Manpower$Country)
[1] United States  Russia         China          India          France         United Kingdom
133 Levels: Afghanistan Albania Algeria Angola Argentina Armenia Australia Austria Azerbaijan Bahrain Bangladesh ... Zimbabwe


  • Make states all uppercase and save result:
# to states_upper
states_upper <- toupper(Manpower$Country)
head(states_upper)
[1] "UNITED STATES"  "RUSSIA"         "CHINA"          "INDIA"          "FRANCE"         "UNITED KINGDOM"


  • Make states_upper all lowercase again:
states_lower <- tolower(Manpower$Country)
head(states_lower)
[1] "united states"  "russia"         "china"          "india"          "france"         "united kingdom"


  • Look at the head of Infrastructure:
head(Infrastructure)


  • Detect all “Republic” in Country:
str_detect(Infrastructure$Country, "Republic")
  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [21] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [41] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
 [81] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[101] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
[121] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE


  • In the Country column, replace “Republic” with “R”…:
Infrastructure$Country <- str_replace(Infrastructure$Country, "Republic", "R")


Valid Values

  • In R, represented as NA
  • May appear in other forms
    • N/A (Excel)
    • Single dot (SPSS, SAS)
    • Empty string


  • Inf - “Infinite value” (indicative of outliers?):
1/0
[1] Inf
1/0 + 1/0
[1] Inf
33333^33333
[1] Inf


  • NaN - “Not a number” (rethink a variable?):
0/0
[1] NaN
1/0 - 1/0
[1] NaN


  • character:
"treatment", "123", "A"


  • numeric:
23.44, 120, NaN, Inf


  • integer:
4L, 1123L


  • factor:
factor("Hello"), factor(8)


  • logical:
TRUE, FALSE, NA


Missing Values


  • Data:
name = c("Jerry", "Beth", "Rick", "Morty")
n_friends = c(NaN, NA, Inf, 2)
status = c("Listening to human music", "Happy Family", "Garage", "")
social_df = data.frame(cbind(name, n_friends, status))


  • Call is.na() on the full social_df to spot all NAs:
is.na(social_df)
      name n_friends status
[1,] FALSE     FALSE  FALSE
[2,] FALSE      TRUE  FALSE
[3,] FALSE     FALSE  FALSE
[4,] FALSE     FALSE  FALSE


  • Use the any() function to ask whether there are any NAs in the data:
any(is.na(social_df))
[1] TRUE


  • View a summary() of the dataset:
summary(social_df)
    name   n_friends                      status 
 Beth :1   2   :1                            :1  
 Jerry:1   Inf :1    Garage                  :1  
 Morty:1   NaN :1    Happy Family            :1  
 Rick :1   NA's:1    Listening to human music:1  


  • Call table() on the status column:
table(social_df$status)

                                           Garage             Happy Family Listening to human music 
                       1                        1                        1                        1 


  • Replace all empty strings in status with NA:
social_df$status[social_df$status == ""] <- NA


  • Print social_df to the console:
social_df


  • Use complete.cases() to see which rows have no missing values:
complete.cases(social_df)
[1]  TRUE FALSE  TRUE FALSE


  • Use na.omit() to remove all rows with any missing values:
na.omit(social_df)

Outliers


  • Data:
Infrastructure = read.csv2("Infrastructure.csv")


  • Histogram:
hist(Infrastructure$Ports)

  • Boxplot:
boxplot(Infrastructure$Airports)

  • Scatterplot:
plot(Infrastructure$Railway_Coverage, Infrastructure$Roadway_Coverage)
LS0tDQp0aXRsZTogIioqMDQuIERhdGEgQ2xlYW5pbmcqKiINCnN1YnRpdGxlOiAiUjEwMSINCmF1dGhvcjogIlbDrXQgR2FicmhlbCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdGhlbWU6IHlldGkgICAgDQogICAgY29kZV9mb2xkaW5nOiAic2hvdyINCi0tLQ0KLS0tDQojIFNldHRpbmcgdXAgdGhlIGxpYnJhcnkNCjxicj4NCg0KKiAqKlBhY2thZ2VzKio6DQpgYGB7ciBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0V9DQojIExvYWQgcGFja2FnZXMgdmlhIGZ1bmN0aW9uDQppcGFrIDwtIGZ1bmN0aW9uKHBrZyl7DQogIG5ldy5wa2cgPC0gcGtnWyEocGtnICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCAiUGFja2FnZSJdKV0NCiAgaWYgKGxlbmd0aChuZXcucGtnKSkgDQogICAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBzYXBwbHkocGtnLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQp9DQoNCnBhY2thZ2VzIDwtIGMoImdncGxvdDIiLCAiZHBseXIiLCAicHN5Y2giLCANCiAgICAgICAgICAgICAgInN1bW1hcnl0b29scyIsICJyZWFkeGwiLCAibHVicmlkYXRlIiwNCiAgICAgICAgICAgICAgInN0cmluZ3IiKQ0KDQppcGFrKHBhY2thZ2VzKQ0KYGBgDQo8YnI+DQoNCiMgRGF0YSBFeHBsb3JhdGlvbg0KIyMgKkV4cGxvcmF0aW9uIG9mIHRoZSByYXcgZGF0YSAtIFtiYXNlXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLWRldmVsL2xpYnJhcnkvYmFzZS9odG1sLzAwSW5kZXguaHRtbCkgYW5kIFtkcGx5cl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2RwbHlyL2RwbHlyLnBkZikqDQo8YnI+DQoNCiogKipEYXRhKiogZW50cnk6DQpgYGB7cn0NCmJtaV8xID0gcmVhZF9leGNlbCgiYm1pLnhsc3giLCBzaGVldCA9IDIpDQpgYGANCjxicj4NCg0KKiBDaGVjayB0aGUgKipjbGFzcyoqIG9mICpibWkqOg0KYGBge3J9DQpjbGFzcyhibWlfMSkNCmBgYA0KPGJyPg0KDQoqIENoZWNrIHRoZSAqKmRpbWVuc2lvbnMqKiBvZiAqYm1pKjoNCmBgYHtyfQ0KZGltKGJtaV8xKQ0KYGBgDQo8YnI+DQoNCiogVmlldyB0aGUgKipjb2x1bW4gbmFtZXMqKiBvZiAqYm1pKjoNCmBgYHtyfQ0KY29sbmFtZXMoYm1pXzEpDQpgYGANCjxicj4NCg0KKiAqKlN0cnVjdHVyZSoqIG9mIHRoZSBkYXRhOg0KYGBge3J9DQpzdHIoYm1pXzEpDQpgYGANCjxicj4NCg0KKiAqKkdsaW1wc2UqKjoNCmBgYHtyfQ0KIyBsaWJyYXJ5KGRwbHlyKSwgaW5zdGFsbC5wYWNrYWdlcyjigJxkcGx5cuKAnCkNCmdsaW1wc2UoYm1pXzEpDQpgYGANCjxicj4NCg0KKiAqKlN1bW1hcnkqKjoNCmBgYHtyfQ0Kc3VtbWFyeShibWlfMSkNCmBgYA0KPGJyPg0KDQoqICoqRmlyc3QqKiAxMCByb3dzOg0KYGBge3J9DQpoZWFkKGJtaV8xLCBuID0gMTApDQpgYGANCjxicj4NCg0KKiAqKkxhc3QqKiAxMCByb3dzOg0KYGBge3J9DQp0YWlsKGJtaV8xLCBuID0gMTApDQpgYGANCjxicj4NCg0KIyMgKkV4cGxvcmF0aW9uIG9mIHRoZSByYXcgZGF0YSAtIFtwc3ljaF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3BzeWNoL3BzeWNoLnBkZikqDQo8YnI+DQoNCiogTG9hZCBwc3ljaDoNCmBgYHtyfQ0KIyBsaWJyYXJ5KHBzeWNoKSwgaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQ0KYGBgDQo8YnI+DQoNCiogQ2hlY2sgdGhlICoqc3RydWN0dXJlKiogb2YgKmJtaSosIHRoZSAqKnBzeWNoKiogd2F5Og0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpkZXNjcmliZShibWlfMSkNCmBgYA0KPGJyPg0KDQoqIENoZWNrIHRoZSAqKnN0cnVjdHVyZSoqIG9mICpibWkqLCB0aGUgKipwc3ljaCoqIHdheSAtICoqYnkgZ3JvdXAqKjoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9DQpibWlfMiA8LSBibWlfMSAlPiUNCiAgICAgICAgIGdyb3VwX2J5KENvdW50cnkpICU+JQ0KICAgICAgICAgbXV0YXRlKEhlYWx0aCA9IGlmX2Vsc2UoQk1JXzE5ODAgPD0gMTguNSB8IEJNSV8xOTgwID49IDI0LjksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVuaGVhbHRoeSIsICJOb3JtYWwiKSkNCg0KDQpkZXNjcmliZS5ieShibWlfMiRCTUlfMTk4MCwgZ3JvdXAgPSBmYWN0b3IoYm1pXzIkSGVhbHRoKSkNCmBgYA0KPGJyPg0KDQojIyAqRXhwbG9yYXRpb24gb2YgdGhlIHJhdyBkYXRhIC0gW3N1bW1hcnl0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3N1bW1hcnl0b29scy9zdW1tYXJ5dG9vbHMucGRmKSoNCjxicj4NCg0KKiBMb2FkIHN1bW1hcnl0b29sczoNCmBgYHtyfQ0KIyBsaWJyYXJ5KHN1bW1hcnl0b29scyksIGluc3RhbGwucGFja2FnZXMoInN1bW1hcnl0b29scyIpDQpgYGANCjxicj4NCg0KKiBEYXRhOg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpNYW5wb3dlciA8LSByZWFkLmNzdignTWFucG93ZXIuY3N2JykNCmBgYA0KPGJyPg0KDQoqIENoZWNrIHRoZSAqKnN0cnVjdHVyZSoqIG9mICpibWkqLCB0aGUgKipzdW1tYXJ5dG9vbHMqKiB3YXk6DQpgYGB7ciwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnZpZXcoZGZTdW1tYXJ5KE1hbnBvd2VyKSkNCmBgYA0KDQojIEludHJvZHVjdGlvbiB0byBEYXRhIFdyYW5nbGluZw0KPGJyPg0KDQoqIERhdGE6DQpgYGB7cn0NCkluZnJhc3RydWN0dXJlIDwtIHJlYWQuY3N2MigiSW5mcmFzdHJ1Y3R1cmUuY3N2IikNCmBgYA0KPGJyPg0KDQoqIFByZXZpZXcgKkluZnJhc3RydWN0dXJlKiB3aXRoICoqc3RyKCkqKjoNCmBgYHtyfQ0Kc3RyKEluZnJhc3RydWN0dXJlKQ0KYGBgDQo8YnI+DQoNCiogQ29lcmNlICpDb3VudHJ5KiB0byAqKmNoYXJhY3RlcioqOg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpJbmZyYXN0cnVjdHVyZSRDb3VudHJ5IDwtIGFzLmNoYXJhY3RlcihJbmZyYXN0cnVjdHVyZSRDb3VudHJ5KQ0KYGBgDQo8YnI+DQoNCiogQ29lcmNlICpSYW5rKiB0byAqKmZhY3RvcioqOg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpJbmZyYXN0cnVjdHVyZSRSYW5rIDwtIGFzLmNoYXJhY3RlcihJbmZyYXN0cnVjdHVyZSRSYW5rKQ0KYGBgDQo8YnI+DQoNCiogTG9vayBhdCAqSW5mcmFzdHJ1Y3R1cmUqIG9uY2UgbW9yZSB3aXRoICoqc3RyKCkqKjoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0Kc3RyKEluZnJhc3RydWN0dXJlKQ0KYGBgDQo8YnI+DQoNCiMjICpTdHJpbmdzKg0KPGJyPg0KDQoqIExvYWQgdGhlIHN0cmluZ3IgcGFja2FnZToNCmBgYHtyfQ0KIyBsaWJyYXJ5KCJzdHJpbmdyIikgaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIpDQpgYGANCjxicj4NCg0KKiAqKlRyaW0qKiBhbGwgbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZToNCmBgYHtyfQ0KbmFtZSA9IGMoIiBGaWxpcCAiLCAiTmljayAiLCAiIEpvbmF0aGFuIikNCnN0cl90cmltKG5hbWUpDQpgYGANCjxicj4NCg0KKiAqKlBhZCoqIHRoZXNlIHN0cmluZ3Mgd2l0aCBsZWFkaW5nIHplcm9zOg0KYGBge3J9DQpwYWQgPSBjKCIyMzQ4NVciLCAiODgyMzQ1M1EiLCAiOTk0WiIpDQoNCnN0cl9wYWQocGFkLCB3aWR0aCA9IDksIHNpZGUgPSAibGVmdCIsIHBhZCA9ICIwIikNCmBgYA0KPGJyPg0KDQoqICoqUHJpbnQqKiBzdGF0ZSBhYmJyZXZpYXRpb25zOg0KYGBge3J9DQpoZWFkKE1hbnBvd2VyJENvdW50cnkpDQpgYGANCjxicj4NCg0KKiBNYWtlIHN0YXRlcyBhbGwgKip1cHBlcmNhc2UqKiBhbmQgc2F2ZSByZXN1bHQ6DQpgYGB7cn0NCiMgdG8gc3RhdGVzX3VwcGVyDQpzdGF0ZXNfdXBwZXIgPC0gdG91cHBlcihNYW5wb3dlciRDb3VudHJ5KQ0KaGVhZChzdGF0ZXNfdXBwZXIpDQpgYGANCjxicj4NCg0KKiBNYWtlIHN0YXRlc191cHBlciBhbGwgKipsb3dlcmNhc2UqKiBhZ2FpbjoNCmBgYHtyfQ0Kc3RhdGVzX2xvd2VyIDwtIHRvbG93ZXIoTWFucG93ZXIkQ291bnRyeSkNCmhlYWQoc3RhdGVzX2xvd2VyKQ0KYGBgDQo8YnI+DQoNCiogTG9vayBhdCB0aGUgKipoZWFkKiogb2YgKkluZnJhc3RydWN0dXJlKjoNCmBgYHtyfQ0KaGVhZChJbmZyYXN0cnVjdHVyZSkNCmBgYA0KPGJyPg0KDQoqICoqRGV0ZWN0KiogYWxsICJSZXB1YmxpYyIgaW4gKkNvdW50cnkqOg0KYGBge3J9DQpzdHJfZGV0ZWN0KEluZnJhc3RydWN0dXJlJENvdW50cnksICJSZXB1YmxpYyIpDQpgYGANCjxicj4NCg0KKiBJbiB0aGUgQ291bnRyeSBjb2x1bW4sICoqcmVwbGFjZSoqICJSZXB1YmxpYyIgd2l0aCAiUiIuLi46DQpgYGB7cn0NCkluZnJhc3RydWN0dXJlJENvdW50cnkgPC0gc3RyX3JlcGxhY2UoSW5mcmFzdHJ1Y3R1cmUkQ291bnRyeSwgIlJlcHVibGljIiwgIlIiKQ0KYGBgDQo8YnI+DQoNCiMjICpWYWxpZCBWYWx1ZXMqDQoNCiogSW4gUiwgcmVwcmVzZW50ZWQgYXMgTkENCiogTWF5IGFwcGVhciBpbiBvdGhlciBmb3Jtcw0KICArIE4vQSAoRXhjZWwpDQogICsgU2luZ2xlIGRvdCAoU1BTUywgU0FTKQ0KICArIEVtcHR5IHN0cmluZw0KICANCjxicj4NCg0KKiAqKkluZioqIC0gIkluZmluaXRlIHZhbHVlIiAoaW5kaWNhdGl2ZSBvZiBvdXRsaWVycz8pOg0KYGBge3J9DQoxLzANCg0KMS8wICsgMS8wDQoNCjMzMzMzXjMzMzMzDQpgYGANCjxicj4NCg0KKiAqKk5hTioqIC0gIk5vdCBhIG51bWJlciIgKHJldGhpbmsgYSB2YXJpYWJsZT8pOg0KYGBge3J9DQowLzANCg0KMS8wIC0gMS8wDQpgYGANCjxicj4NCg0KKiAqKmNoYXJhY3RlcioqOg0KYGBge3IgZXZhbD1GQUxTRX0NCiJ0cmVhdG1lbnQiLCAiMTIzIiwgIkEiDQpgYGANCjxicj4NCg0KKiAqKm51bWVyaWMqKjogDQpgYGB7ciBldmFsPUZBTFNFfQ0KMjMuNDQsIDEyMCwgTmFOLCBJbmYNCmBgYA0KPGJyPg0KDQoqICoqaW50ZWdlcioqOiANCmBgYHtyIGV2YWw9RkFMU0V9DQo0TCwgMTEyM0wNCmBgYA0KPGJyPg0KDQoqICoqZmFjdG9yKio6DQpgYGB7ciBldmFsPUZBTFNFfQ0KZmFjdG9yKCJIZWxsbyIpLCBmYWN0b3IoOCkNCmBgYA0KPGJyPg0KDQoqICoqbG9naWNhbCoqOg0KYGBge3IgZXZhbD1GQUxTRX0NClRSVUUsIEZBTFNFLCBOQQ0KYGBgDQoNCjxicj4NCg0KIyMgKk1pc3NpbmcgVmFsdWVzKg0KDQo8YnI+DQoNCiogKipEYXRhKio6DQpgYGB7cn0NCm5hbWUgPSBjKCJKZXJyeSIsICJCZXRoIiwgIlJpY2siLCAiTW9ydHkiKQ0Kbl9mcmllbmRzID0gYyhOYU4sIE5BLCBJbmYsIDIpDQpzdGF0dXMgPSBjKCJMaXN0ZW5pbmcgdG8gaHVtYW4gbXVzaWMiLCAiSGFwcHkgRmFtaWx5IiwgIkdhcmFnZSIsICIiKQ0Kc29jaWFsX2RmID0gZGF0YS5mcmFtZShjYmluZChuYW1lLCBuX2ZyaWVuZHMsIHN0YXR1cykpDQpgYGANCg0KPGJyPg0KDQoqIENhbGwgKippcy5uYSgpKiogb24gdGhlICoqZnVsbCoqICpzb2NpYWxfZGYqIHRvICoqc3BvdCBhbGwgTkFzKio6DQpgYGB7cn0NCmlzLm5hKHNvY2lhbF9kZikNCmBgYA0KDQo8YnI+DQoNCiogVXNlIHRoZSAqKmFueSgpKiogZnVuY3Rpb24gdG8gYXNrIHdoZXRoZXIgdGhlcmUgYXJlICoqYW55IE5BcyoqIGluIHRoZSBkYXRhOg0KYGBge3J9DQphbnkoaXMubmEoc29jaWFsX2RmKSkNCmBgYA0KDQo8YnI+DQoNCiogVmlldyBhICoqc3VtbWFyeSgpKiogb2YgdGhlIGRhdGFzZXQ6DQpgYGB7cn0NCnN1bW1hcnkoc29jaWFsX2RmKQ0KYGBgDQoNCjxicj4NCg0KKiBDYWxsICoqdGFibGUoKSoqIG9uIHRoZSAqc3RhdHVzKiBjb2x1bW46DQpgYGB7cn0NCnRhYmxlKHNvY2lhbF9kZiRzdGF0dXMpDQpgYGANCg0KPGJyPg0KDQoqICoqUmVwbGFjZSBhbGwgZW1wdHkgc3RyaW5ncyoqIGluICpzdGF0dXMqIHdpdGggKipOQSoqOg0KYGBge3J9DQpzb2NpYWxfZGYkc3RhdHVzW3NvY2lhbF9kZiRzdGF0dXMgPT0gIiJdIDwtIE5BDQpgYGANCg0KPGJyPg0KDQoqICoqUHJpbnQqKiAqc29jaWFsX2RmKiB0byB0aGUgY29uc29sZToNCmBgYHtyfQ0Kc29jaWFsX2RmDQpgYGANCg0KPGJyPg0KDQoqIFVzZSAqKmNvbXBsZXRlLmNhc2VzKCkqKiB0byBzZWUgd2hpY2ggKipyb3dzIGhhdmUgbm8gbWlzc2luZyB2YWx1ZXMqKjoNCmBgYHtyfQ0KY29tcGxldGUuY2FzZXMoc29jaWFsX2RmKQ0KYGBgDQoNCjxicj4NCg0KKiBVc2UgKipuYS5vbWl0KCkqKiB0byAqKnJlbW92ZSBhbGwgcm93cyoqIHdpdGggKiphbnkgbWlzc2luZyB2YWx1ZXMqKjoNCmBgYHtyfQ0KbmEub21pdChzb2NpYWxfZGYpDQpgYGANCg0KIyMgKk91dGxpZXJzKg0KDQo8YnI+DQoNCiogKipEYXRhKio6DQpgYGB7cn0NCkluZnJhc3RydWN0dXJlID0gcmVhZC5jc3YyKCJJbmZyYXN0cnVjdHVyZS5jc3YiKQ0KYGBgDQoNCjxicj4NCg0KKiAqKkhpc3RvZ3JhbSoqOg0KYGBge3J9DQpoaXN0KEluZnJhc3RydWN0dXJlJFBvcnRzKQ0KYGBgDQoNCiogKipCb3hwbG90Kio6DQpgYGB7cn0NCmJveHBsb3QoSW5mcmFzdHJ1Y3R1cmUkQWlycG9ydHMpDQpgYGANCg0KKiAqKlNjYXR0ZXJwbG90Kio6DQpgYGB7cn0NCnBsb3QoSW5mcmFzdHJ1Y3R1cmUkUmFpbHdheV9Db3ZlcmFnZSwgSW5mcmFzdHJ1Y3R1cmUkUm9hZHdheV9Db3ZlcmFnZSkNCmBgYA0KDQojIyBSZXNvdXJjZXMNCg0KW0RhdGFDYW1wJ3MgRnJlZSBDbGFzc3Jvb20gTW9kZWwgVXNlZCBpbiAxODAgQ291bnRyaWVzXShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY29tbXVuaXR5L2Jsb2cvZGF0YWNhbXBzLWZyZWUtY2xhc3Nyb29tLW1vZGVsLXVzZWQtaW4tMTgwLWNvdW50cmllcz9mYmNsaWQ9SXdBUjFCbzBCNC1EMk03TERBR1VnNkxWYllLeWVKbjRPNkZZeEtzMHdxRWdBN3UwRXpNM2NVT3pIU0dLWSkNCjxicj4NCg0KW1RoaXMgUiBEYXRhIEltcG9ydCBUdXRvcmlhbCBJcyBFdmVyeXRoaW5nIFlvdSBOZWVkXShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY29tbXVuaXR5L3R1dG9yaWFscy9yLWRhdGEtaW1wb3J0LXR1dG9yaWFsKQ0KPGJyPg0KDQpbVGhlIExhbmRzY2FwZSBvZiBSIFBhY2thZ2VzIGZvcg0KQXV0b21hdGVkIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCl0oaHR0cHM6Ly9qb3VybmFsLnItcHJvamVjdC5vcmcvYXJjaGl2ZS8yMDE5L1JKLTIwMTktMDMzL1JKLTIwMTktMDMzLnBkZikNCg0KDQoNCg==