Setup

Loading the necessary packages to reproduce the report:

library(readr)
library(tidyr)
library(dplyr)
library(Hmisc)
library(deductive)
library(validate)
library(outliers)
library(kableExtra)
library(car)

Read WHO Data

Importing the WHO.csv dataset using readr.

# This is an R chunk for reading the WHO data.
WHO <- read_csv("WHO.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  country = col_character(),
  iso2 = col_character(),
  iso3 = col_character()
)
See spec(...) for full column specifications.
head(WHO)
NA

Tidy Task 1:

In WHO dataset, from 5th column to the last column represents values instead of variables, thus those columns were transformed from wide to long format by using gather() function.

# Task 1
WHO_1 <- WHO %>% gather(code, value, 5:60) 
WHO_1

Tidy Task 2:

Now, each value in the new column “code” containes separate pieces of information which are separated by "_" sign. Concatenated values in “code” column was stored separated separate() function and stored in three new columns(“New”, “CaseType”, “sex_age”). The column “sex_age” contains combined values of “sex” and “age”, separate() function was used again to split these values into gender and age group.

# Task 2
WHO_1 <- WHO_1 %>% separate(code , into= c("New", "CaseType", "sex_age"), sep = "_")
WHO_1 <- separate(WHO_1, 'sex_age', into = c("Sex","Age"), sep = 1) 
WHO_1

Tidy Task 3:

The “rel”, “ep”, “sn”, and “sp” keys need to be in their own columns as we will treat each of these as a separate variable. The column “CaseType” contains these keys.To decompose CaseType in various columns spread() function is used.

# Task 3
WHO_1 <- WHO_1 %>% spread('CaseType', value)

Tidy Task 4:

To achieve final Tidy format, the two categorical variables “Sex” and “Age”, needed to be converted as factors. Apporipiate steps are performed to achieve this using mutate() function (as mentioned in the requirements).

# Task 4

#Factorizing and labeling age column
WHO_1 <- WHO_1 %>% mutate(Age = factor(Age, levels = c("014","1524","2534","3544","4554","5564","65"), labels = c("<15","15-24","25-34","35-44","45-54","55-64","65>="), ordered = TRUE))

#Factorizing gender column
WHO_1 <- WHO_1 %>% mutate(Sex = factor(Sex))
WHO_1
class(WHO_1$Sex)
[1] "factor"
class(WHO_1$Age)
[1] "ordered" "factor" 

Task 5: Filter & Select

The columns iso2 and New are redundant and hence those were dropped using select() function. Three countries were selected using filter() function.


# Task 5 
WHO_subset <- WHO_1 %>% select( -(iso2), -(New)) %>% filter( country %in% c("Sri Lanka", "Romania", "Portugal"))
head(WHO_subset)
NA

Dimension of the subsetted dataset.

dim(WHO_subset)
[1] 1428    9

Read Species and Surveys data sets

Importing the Species and Surveys data sets using readr.


# Reading the Species and Surveys data sets.
species <- read_csv("species.csv")
Parsed with column specification:
cols(
  species_id = col_character(),
  genus = col_character(),
  species = col_character(),
  taxa = col_character()
)
head(species)

surveys <- read_csv("surveys.csv")
Parsed with column specification:
cols(
  record_id = col_double(),
  month = col_double(),
  day = col_double(),
  year = col_double(),
  species_id = col_character(),
  sex = col_character(),
  hindfoot_length = col_double(),
  weight = col_double()
)
head(surveys)
NA
NA

Task 6: Join

To combine surveys and species data frames, left_join() function was used with the key variable species_id. The combined data frame was renamed as surveys_combined.

# Task 6.
surveys_combined <- left_join(surveys , species, by="species_id")
head(surveys_combined)
str(surveys_combined)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    35549 obs. of  11 variables:
 $ record_id      : num  1 2 3 4 5 6 7 8 9 10 ...
 $ month          : num  7 7 7 7 7 7 7 7 7 7 ...
 $ day            : num  16 16 16 16 16 16 16 16 16 16 ...
 $ year           : num  1977 1977 1977 1977 1977 ...
 $ species_id     : chr  "NL" "NL" "DM" "DM" ...
 $ sex            : chr  "M" "M" "F" "M" ...
 $ hindfoot_length: num  32 33 37 36 35 14 NA 37 34 20 ...
 $ weight         : num  NA NA NA NA NA NA NA NA NA NA ...
 $ genus          : chr  "Neotoma" "Neotoma" "Dipodomys" "Dipodomys" ...
 $ species        : chr  "albigula" "albigula" "merriami" "merriami" ...
 $ taxa           : chr  "Rodent" "Rodent" "Rodent" "Rodent" ...

Task 7: Calculate

The variable “month” of “surveys_combined” is recorded as a numerical variable, to get monthly average, “month” column was converted as a factor. Then the columns “month”, “weight” and “hindfoot_length”, fitered by the species_id=“NL”, was stored in a new data frame “NL”. Aggregate function was used to compute monthly averages of weight and hindfoot length in a new data frame.


# Task 7.
surveys_combined$month <- factor(surveys_combined$month) # Factorizing the "month" column
#str(surveys_combined$month)
NL <- surveys_combined %>% filter(species_id == "NL") #Filtering the dataset by species id = "NL"

The NL dataframe is derived according to the specifications. Monthly averages of weight and hindfoot length variables will be calculated from the following code.


NL <- NL %>%select(month, weight, hindfoot_length) # Selecting the columns month, weight and hindfoot_length
# Storing the monthly averages of weight and hindfoot length in a new data frame
NL_averages <- aggregate(NL[,c("weight","hindfoot_length")], by = list(NL$month), FUN = mean, na.rm=TRUE)
NL_averages <- NL_averages %>% rename(Month = Group.1, Average_weight = weight, Average_hindfoot_length = hindfoot_length)
NL_averages %>% kable() %>% kable_styling(bootstrap_options = "condensed")#Output the results
Month Average_weight Average_hindfoot_length
1 179.3443 32.54098
2 181.3818 32.82353
3 177.4516 32.75862
4 153.0690 32.02439
5 142.7536 31.60000
6 143.7879 32.18889
7 141.7415 32.35398
8 152.5100 32.07143
9 164.9920 32.50427
10 169.1364 32.43119
11 170.6373 32.59574
12 175.1515 32.34848

Task 8: Missing Values

The dataframe surveys_combined was subsetted by selecting records only for the year 1989 and renamed as surveys_combined_year. Then, the total missing values in the weight column of surveys_combined_year data frame grouped by species were calculated. Missing values in the weight column were replaced with the mean values of each species. Imputed data was saved as surveys_weight_imputed.

# Task 8 
surveys_combined$year <- factor(surveys_combined$year) # Factorizing the "year" column
surveys_combined_year <- surveys_combined %>% filter(year == "1989") # Filtering the year
head(surveys_combined_year)

kable(surveys_combined_year %>% group_by(species_id) %>% summarise(Missing_values = sum(is.na(weight)))) %>% kable_styling(bootstrap_options = "condensed", full_width = FALSE, position = "left") # Total missing values in weight column grouped by species_id

species_id Missing_values
AB 31
AH 30
BA 0
CB 1
CS 1
DM 6
DO 2
DS 4
NL 6
OL 0
OT 1
OX 0
PC 1
PE 6
PF 0
PH 0
PM 0
PP 1
RF 0
RM 9
SA 3
SC 1
SF 0
SH 0
SS 18
UP 2
NA 7


weight_mean_by_species <- aggregate(surveys_combined_year[,c("weight")], by = list(surveys_combined_year$species_id), FUN = mean, na.rm=TRUE)
weight_mean_by_species <- weight_mean_by_species %>% rename(species_id = Group.1)# A new dataframe with average weights by species id for the year 1989
weight_mean_by_species %>% kable() %>% kable_styling(bootstrap_options = "condensed", full_width = FALSE, position = "left")

species_id weight
AB NaN
AH NaN
BA 7.000000
CB NaN
CS NaN
DM 44.349206
DO 51.025641
DS 121.896552
NL 151.278688
OL 31.947368
OT 24.663366
OX 20.000000
PC NaN
PE 21.935672
PF 7.230769
PH 30.500000
PM 20.222222
PP 17.409091
RF 13.346939
RM 10.411311
SA NaN
SC NaN
SF 54.800000
SH 76.345455
SS NaN
UP NaN


surveys_weight_imputed <- surveys_combined_year %>% group_by(species_id) %>% mutate(weight = ifelse(is.na(weight), mean(weight, na.rm = TRUE), weight)) # Mean by species type imputation for weight column

#surveys_weight_imputed <- left_join(surveys_combined_year, weight_mean_by_species, by= "species_id") %>% mutate(weight = ifelse(is.na(weight.x), weight.y, weight.x)) %>% select(-weight.y, -weight.x) # adding weight_mean_by_species dataset to surveys_combined_year dataset to fill the missing values in the weight column
head(surveys_weight_imputed)
View(surveys_weight_imputed)

Checking Imputation As we have replaced the missing values in the “weight” column with the mean values by species type, the count of total NA values in surveys_weight_imputed df’s weight column should be less than that value of surveys_combined_year. However, it can be noticed that the NA count is non zero in the weight column of surveys_weight_imputed data frame. This issue will be discussed in the next task.

sum(is.na(surveys_combined_year$weight))
[1] 130
sum(is.na(surveys_weight_imputed$weight))
[1] 95

Task 9: Special Values

Inspecting the weight column in surveys_weight_imputed dataframe for any special values (i.e., NaN, Inf, -Inf).

# Task 9

cat("missing value count in weights of surveys_combined_year: ", sum(is.na(surveys_combined_year$weight)))
missing value count in weights of surveys_combined_year:  130
cat("\nSpecial value count in weights of surveys_weight_imputed: ",sum(is.nan(surveys_weight_imputed$weight)))

Special value count in weights of surveys_weight_imputed:  95

It can be noticed that some of the missing values are recorded as special values, “NAN” which stands for “Not A Number”" and is a logical vector of a length 1 and applies to numerical values. In part 8 when we calculated the mean using aggrigate() function by passing an argument na.rm=TRUE, which ignores missing values. There are groups in surveys_combined_year in which all values in weight column are missing values. The groups in our case are (“AB”, “AH”,“CB”,“CS”). When applying the function mean(….,na.rm=TRUE) on the fore mentioned species_id’s weight column, as all values are NA’s, it will select zero values and divide it by total values selected(0), which will return “NAN”.

Task 10: Outliers

In the surveys_combined data frame, the variable hindfoot length was inspected for possible univariate outliers. Different approaches can be used to detect univariate outliers such as; - Tukey’s method of outlier detection - Box plots - z-score method

First a boxplot was constructed to visually identify whether there are possible outliers in the data set. Very few number of outliers were visible in the boxplot. In addition, the boxplot depicted a skewness in the distribution of the hindfoot_length variable, which does not support the normality assumption. And hence for the outlier detection task, the two non parametric methods, Tukey’s method and boxplot were used as they don’t require any assumptions about the distribution.

OutVals = boxplot(surveys_combined$hindfoot_length,main="Boxplot for hindfoot_length", ylab="Hindfoot Length", col ="seagreen")$out

which(surveys_combined$hindfoot_length %in% OutVals)
[1] 10574 30425
Boxplot(surveys_combined$hindfoot_length,id=TRUE, ylab="Hindfoot Length", col ="seagreen")#boxplot with location of the outliers
[1] 10574 30425

It is visible from the Boxplot that 2 observations are outside of the top wisker which denotes (Q3+1.5xIQR). Hence, qualified to be considered as outliers according to Tukey’s Method.

Handeling Outliers

Only two outliers were detected and it is a very small amount when compared to the complete dataset surveys_combined which has over 31,000 observations. Thus the effect of the outlier on the data distribution parameters will be negligible. Hence, excluding outliers will be the best suited approach to handle outliers in dataset surveys_combined.

# Task 10. 

summary(surveys_combined$hindfoot_length) # summary statistics before removing outliers
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   2.00   21.00   32.00   29.29   36.00   70.00    4111 
surveys_combined <- surveys_combined[-c(10574,30425),] #Excluding outliers(rows) from the dataframe
summary(surveys_combined$hindfoot_length) # summary statistics after removing outliers
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   2.00   21.00   32.00   29.29   36.00   58.00    4111 
surveys_combined$hindfoot_length %>%  boxplot(main="Box Plot of Hindfoot Length", ylab="Hindfoot Length", col = "lightblue") # checking the imputed dataset



LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMiwgMjAxOSIKYXV0aG9yOiAiQm9kaXlhYmFkdWdlIERld3NyaSBMYWxpdGhpIFBlcmVyYShTMzc2Mjg5MCkgJiBBeXVzaCBSYW5qYW4oUzM4MDI1NDEpIgpzdWJ0aXRsZTogQXNzaWdubWVudCAyCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKIyMgU2V0dXAKCkxvYWRpbmcgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyB0byByZXByb2R1Y2UgdGhlIHJlcG9ydDoKCmBgYHtyLCBlY2hvID0gVFJVRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyZWFkcikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShIbWlzYykKbGlicmFyeShkZWR1Y3RpdmUpCmxpYnJhcnkodmFsaWRhdGUpCmxpYnJhcnkob3V0bGllcnMpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShjYXIpCmBgYAoKCiMjIFJlYWQgV0hPIERhdGEKCkltcG9ydGluZyB0aGUgV0hPLmNzdiBkYXRhc2V0IHVzaW5nIHJlYWRyLgoKYGBge3J9CiMgVGhpcyBpcyBhbiBSIGNodW5rIGZvciByZWFkaW5nIHRoZSBXSE8gZGF0YS4KV0hPIDwtIHJlYWRfY3N2KCJXSE8uY3N2IikKaGVhZChXSE8pCgpgYGAKCgoKIyMgVGlkeSBUYXNrIDE6CgpJbiBXSE8gZGF0YXNldCwgZnJvbSA1dGggY29sdW1uIHRvIHRoZSBsYXN0IGNvbHVtbiByZXByZXNlbnRzIHZhbHVlcyBpbnN0ZWFkIG9mIHZhcmlhYmxlcywgdGh1cyB0aG9zZSBjb2x1bW5zIHdlcmUgdHJhbnNmb3JtZWQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0IGJ5IHVzaW5nIGdhdGhlcigpIGZ1bmN0aW9uLgpgYGB7ciwgZWNobz1UUlVFfQojIFRhc2sgMQpXSE9fMSA8LSBXSE8gJT4lIGdhdGhlcihjb2RlLCB2YWx1ZSwgNTo2MCkgCldIT18xCmBgYAoKCgojIyBUaWR5IFRhc2sgMjoKCk5vdywgZWFjaCB2YWx1ZSBpbiB0aGUgbmV3IGNvbHVtbiAiY29kZSIgY29udGFpbmVzIHNlcGFyYXRlIHBpZWNlcyBvZiBpbmZvcm1hdGlvbiB3aGljaCBhcmUgc2VwYXJhdGVkIGJ5ICJfIiBzaWduLiBDb25jYXRlbmF0ZWQgdmFsdWVzIGluICJjb2RlIiBjb2x1bW4gd2FzIHN0b3JlZCBzZXBhcmF0ZWQgc2VwYXJhdGUoKSBmdW5jdGlvbiBhbmQgc3RvcmVkIGluIHRocmVlIG5ldyBjb2x1bW5zKCJOZXciLCAiQ2FzZVR5cGUiLCAic2V4X2FnZSIpLgpUaGUgY29sdW1uICJzZXhfYWdlIiBjb250YWlucyBjb21iaW5lZCB2YWx1ZXMgb2YgInNleCIgYW5kICJhZ2UiLCBzZXBhcmF0ZSgpIGZ1bmN0aW9uIHdhcyB1c2VkIGFnYWluIHRvIHNwbGl0IHRoZXNlIHZhbHVlcyBpbnRvIGdlbmRlciBhbmQgYWdlIGdyb3VwLgoKYGBge3J9CiMgVGFzayAyCldIT18xIDwtIFdIT18xICU+JSBzZXBhcmF0ZShjb2RlICwgaW50bz0gYygiTmV3IiwgIkNhc2VUeXBlIiwgInNleF9hZ2UiKSwgc2VwID0gIl8iKQpXSE9fMSA8LSBzZXBhcmF0ZShXSE9fMSwgJ3NleF9hZ2UnLCBpbnRvID0gYygiU2V4IiwiQWdlIiksIHNlcCA9IDEpIApXSE9fMQpgYGAKCgoKIyMgVGlkeSBUYXNrIDM6CgpUaGUg4oCccmVs4oCdLCDigJxlcOKAnSwg4oCcc27igJ0sIGFuZCDigJxzcOKAnSBrZXlzIG5lZWQgdG8gYmUgaW4gdGhlaXIgb3duIGNvbHVtbnMgYXMgd2Ugd2lsbCB0cmVhdCBlYWNoIG9mIHRoZXNlIGFzIGEgc2VwYXJhdGUgdmFyaWFibGUuClRoZSBjb2x1bW4gIkNhc2VUeXBlIiBjb250YWlucyB0aGVzZSBrZXlzLlRvIGRlY29tcG9zZSBDYXNlVHlwZSBpbiB2YXJpb3VzIGNvbHVtbnMgc3ByZWFkKCkgZnVuY3Rpb24gaXMgdXNlZC4KCmBgYHtyfQojIFRhc2sgMwpXSE9fMSA8LSBXSE9fMSAlPiUgc3ByZWFkKCdDYXNlVHlwZScsIHZhbHVlKQpXSE9fMQpgYGAKCgoKIyMgVGlkeSBUYXNrIDQ6IAoKVG8gYWNoaWV2ZSBmaW5hbCBUaWR5IGZvcm1hdCwgdGhlIHR3byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgIlNleCIgYW5kICJBZ2UiLCBuZWVkZWQgdG8gYmUgY29udmVydGVkIGFzIGZhY3RvcnMuCkFwcG9yaXBpYXRlIHN0ZXBzIGFyZSBwZXJmb3JtZWQgdG8gYWNoaWV2ZSB0aGlzIHVzaW5nIG11dGF0ZSgpIGZ1bmN0aW9uIChhcyBtZW50aW9uZWQgaW4gdGhlIHJlcXVpcmVtZW50cykuCgpgYGB7cn0KIyBUYXNrIDQKCiNGYWN0b3JpemluZyBhbmQgbGFiZWxpbmcgYWdlIGNvbHVtbgpXSE9fMSA8LSBXSE9fMSAlPiUgbXV0YXRlKEFnZSA9IGZhY3RvcihBZ2UsIGxldmVscyA9IGMoIjAxNCIsIjE1MjQiLCIyNTM0IiwiMzU0NCIsIjQ1NTQiLCI1NTY0IiwiNjUiKSwgbGFiZWxzID0gYygiPDE1IiwiMTUtMjQiLCIyNS0zNCIsIjM1LTQ0IiwiNDUtNTQiLCI1NS02NCIsIjY1Pj0iKSwgb3JkZXJlZCA9IFRSVUUpKQoKI0ZhY3Rvcml6aW5nIGdlbmRlciBjb2x1bW4KV0hPXzEgPC0gV0hPXzEgJT4lIG11dGF0ZShTZXggPSBmYWN0b3IoU2V4KSkKV0hPXzEKY2xhc3MoV0hPXzEkU2V4KQpjbGFzcyhXSE9fMSRBZ2UpCmBgYAoKCgojIyBUYXNrIDU6IEZpbHRlciAmIFNlbGVjdAoKVGhlIGNvbHVtbnMgYGlzbzJgIGFuZCBgTmV3YCBhcmUgcmVkdW5kYW50IGFuZCBoZW5jZSB0aG9zZSB3ZXJlIGRyb3BwZWQgdXNpbmcgc2VsZWN0KCkgZnVuY3Rpb24uIFRocmVlIGNvdW50cmllcyB3ZXJlIHNlbGVjdGVkIHVzaW5nIGZpbHRlcigpIGZ1bmN0aW9uLgoKYGBge3J9CgojIFRhc2sgNSAKV0hPX3N1YnNldCA8LSBXSE9fMSAlPiUgc2VsZWN0KCAtKGlzbzIpLCAtKE5ldykpICU+JSBmaWx0ZXIoIGNvdW50cnkgJWluJSBjKCJTcmkgTGFua2EiLCAiUm9tYW5pYSIsICJQb3J0dWdhbCIpKQpoZWFkKFdIT19zdWJzZXQpCgpgYGAKRGltZW5zaW9uIG9mIHRoZSBzdWJzZXR0ZWQgZGF0YXNldC4KYGBge3J9CmRpbShXSE9fc3Vic2V0KQoKYGBgCgoKCiMjIFJlYWQgU3BlY2llcyBhbmQgU3VydmV5cyBkYXRhIHNldHMKCkltcG9ydGluZyB0aGUgKipTcGVjaWVzKiogYW5kICoqU3VydmV5cyoqIGRhdGEgc2V0cyB1c2luZyByZWFkci4KCmBgYHtyfQoKIyBSZWFkaW5nIHRoZSBTcGVjaWVzIGFuZCBTdXJ2ZXlzIGRhdGEgc2V0cy4Kc3BlY2llcyA8LSByZWFkX2Nzdigic3BlY2llcy5jc3YiKQpoZWFkKHNwZWNpZXMpCgpzdXJ2ZXlzIDwtIHJlYWRfY3N2KCJzdXJ2ZXlzLmNzdiIpCmhlYWQoc3VydmV5cykKCgpgYGAKCgoKIyMgVGFzayA2OiBKb2luICAKClRvIGNvbWJpbmUgYHN1cnZleXNgIGFuZCBgc3BlY2llc2AgZGF0YSBmcmFtZXMsIGxlZnRfam9pbigpIGZ1bmN0aW9uIHdhcyB1c2VkIHdpdGggdGhlIGtleSB2YXJpYWJsZSBgc3BlY2llc19pZGAuIFRoZSBjb21iaW5lZCBkYXRhIGZyYW1lIHdhcyByZW5hbWVkIGFzIGBzdXJ2ZXlzX2NvbWJpbmVkYC4KCgpgYGB7cn0KIyBUYXNrIDYuCnN1cnZleXNfY29tYmluZWQgPC0gbGVmdF9qb2luKHN1cnZleXMgLCBzcGVjaWVzLCBieT0ic3BlY2llc19pZCIpCmhlYWQoc3VydmV5c19jb21iaW5lZCkKc3RyKHN1cnZleXNfY29tYmluZWQpCgpgYGAKCgoKIyMgVGFzayA3OiBDYWxjdWxhdGUgCgpUaGUgdmFyaWFibGUgIm1vbnRoIiBvZiAic3VydmV5c19jb21iaW5lZCIgaXMgcmVjb3JkZWQgYXMgYSBudW1lcmljYWwgdmFyaWFibGUsIHRvIGdldCBtb250aGx5IGF2ZXJhZ2UsICJtb250aCIgY29sdW1uIHdhcyBjb252ZXJ0ZWQgYXMgYSBmYWN0b3IuIFRoZW4gdGhlIGNvbHVtbnMgIm1vbnRoIiwgIndlaWdodCIgYW5kICJoaW5kZm9vdF9sZW5ndGgiLCBmaXRlcmVkIGJ5IHRoZSAqKnNwZWNpZXNfaWQ9Ik5MIioqLCB3YXMgc3RvcmVkIGluIGEgbmV3IGRhdGEgZnJhbWUgIk5MIi4gCkFnZ3JlZ2F0ZSBmdW5jdGlvbiB3YXMgdXNlZCB0byBjb21wdXRlIG1vbnRobHkgYXZlcmFnZXMgb2Ygd2VpZ2h0IGFuZCBoaW5kZm9vdCBsZW5ndGggaW4gYSBuZXcgZGF0YSBmcmFtZS4KCgpgYGB7cn0KCiMgVGFzayA3LgpzdXJ2ZXlzX2NvbWJpbmVkJG1vbnRoIDwtIGZhY3RvcihzdXJ2ZXlzX2NvbWJpbmVkJG1vbnRoKSAjIEZhY3Rvcml6aW5nIHRoZSAibW9udGgiIGNvbHVtbgojc3RyKHN1cnZleXNfY29tYmluZWQkbW9udGgpCk5MIDwtIHN1cnZleXNfY29tYmluZWQgJT4lIGZpbHRlcihzcGVjaWVzX2lkID09ICJOTCIpICNGaWx0ZXJpbmcgdGhlIGRhdGFzZXQgYnkgc3BlY2llcyBpZCA9ICJOTCIKCmBgYAoKClRoZSAqKk5MKiogZGF0YWZyYW1lIGlzIGRlcml2ZWQgYWNjb3JkaW5nIHRvIHRoZSBzcGVjaWZpY2F0aW9ucy4gTW9udGhseSBhdmVyYWdlcyBvZiB3ZWlnaHQgYW5kIGhpbmRmb290IGxlbmd0aCB2YXJpYWJsZXMgd2lsbCBiZSBjYWxjdWxhdGVkIGZyb20gdGhlIGZvbGxvd2luZyBjb2RlLgoKCmBgYHtyfQoKTkwgPC0gTkwgJT4lc2VsZWN0KG1vbnRoLCB3ZWlnaHQsIGhpbmRmb290X2xlbmd0aCkgIyBTZWxlY3RpbmcgdGhlIGNvbHVtbnMgbW9udGgsIHdlaWdodCBhbmQgaGluZGZvb3RfbGVuZ3RoCiMgU3RvcmluZyB0aGUgbW9udGhseSBhdmVyYWdlcyBvZiB3ZWlnaHQgYW5kIGhpbmRmb290IGxlbmd0aCBpbiBhIG5ldyBkYXRhIGZyYW1lCk5MX2F2ZXJhZ2VzIDwtIGFnZ3JlZ2F0ZShOTFssYygid2VpZ2h0IiwiaGluZGZvb3RfbGVuZ3RoIildLCBieSA9IGxpc3QoTkwkbW9udGgpLCBGVU4gPSBtZWFuLCBuYS5ybT1UUlVFKQpOTF9hdmVyYWdlcyA8LSBOTF9hdmVyYWdlcyAlPiUgcmVuYW1lKE1vbnRoID0gR3JvdXAuMSwgQXZlcmFnZV93ZWlnaHQgPSB3ZWlnaHQsIEF2ZXJhZ2VfaGluZGZvb3RfbGVuZ3RoID0gaGluZGZvb3RfbGVuZ3RoKQpOTF9hdmVyYWdlcyAlPiUga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJjb25kZW5zZWQiKSNPdXRwdXQgdGhlIHJlc3VsdHMKYGBgCgoKCgojIyBUYXNrIDg6IE1pc3NpbmcgVmFsdWVzCgpUaGUgZGF0YWZyYW1lIGBzdXJ2ZXlzX2NvbWJpbmVkYCB3YXMgc3Vic2V0dGVkIGJ5IHNlbGVjdGluZyByZWNvcmRzIG9ubHkgZm9yIHRoZSB5ZWFyIDE5ODkgYW5kIHJlbmFtZWQgYXMgYHN1cnZleXNfY29tYmluZWRfeWVhcmAuClRoZW4sIHRoZSB0b3RhbCBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgYHdlaWdodGAgY29sdW1uIG9mIGBzdXJ2ZXlzX2NvbWJpbmVkX3llYXJgIGRhdGEgZnJhbWUgZ3JvdXBlZCBieSBgc3BlY2llc2Agd2VyZSBjYWxjdWxhdGVkLiBNaXNzaW5nIHZhbHVlcyBpbiB0aGUgYHdlaWdodGAgY29sdW1uIHdlcmUgcmVwbGFjZWQgd2l0aCB0aGUgbWVhbiB2YWx1ZXMgb2YgZWFjaCBzcGVjaWVzLiBJbXB1dGVkIGRhdGEgd2FzIHNhdmVkIGFzIGBzdXJ2ZXlzX3dlaWdodF9pbXB1dGVkYC4gCgpgYGB7cn0KIyBUYXNrIDggCnN1cnZleXNfY29tYmluZWQkeWVhciA8LSBmYWN0b3Ioc3VydmV5c19jb21iaW5lZCR5ZWFyKSAjIEZhY3Rvcml6aW5nIHRoZSAieWVhciIgY29sdW1uCnN1cnZleXNfY29tYmluZWRfeWVhciA8LSBzdXJ2ZXlzX2NvbWJpbmVkICU+JSBmaWx0ZXIoeWVhciA9PSAiMTk4OSIpICMgRmlsdGVyaW5nIHRoZSB5ZWFyCmhlYWQoc3VydmV5c19jb21iaW5lZF95ZWFyKQoKa2FibGUoc3VydmV5c19jb21iaW5lZF95ZWFyICU+JSBncm91cF9ieShzcGVjaWVzX2lkKSAlPiUgc3VtbWFyaXNlKE1pc3NpbmdfdmFsdWVzID0gc3VtKGlzLm5hKHdlaWdodCkpKSkgJT4lIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAiY29uZGVuc2VkIiwgZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJsZWZ0IikgIyBUb3RhbCBtaXNzaW5nIHZhbHVlcyBpbiB3ZWlnaHQgY29sdW1uIGdyb3VwZWQgYnkgc3BlY2llc19pZAoKd2VpZ2h0X21lYW5fYnlfc3BlY2llcyA8LSBhZ2dyZWdhdGUoc3VydmV5c19jb21iaW5lZF95ZWFyWyxjKCJ3ZWlnaHQiKV0sIGJ5ID0gbGlzdChzdXJ2ZXlzX2NvbWJpbmVkX3llYXIkc3BlY2llc19pZCksIEZVTiA9IG1lYW4sIG5hLnJtPVRSVUUpCndlaWdodF9tZWFuX2J5X3NwZWNpZXMgPC0gd2VpZ2h0X21lYW5fYnlfc3BlY2llcyAlPiUgcmVuYW1lKHNwZWNpZXNfaWQgPSBHcm91cC4xKSMgQSBuZXcgZGF0YWZyYW1lIHdpdGggYXZlcmFnZSB3ZWlnaHRzIGJ5IHNwZWNpZXMgaWQgZm9yIHRoZSB5ZWFyIDE5ODkKd2VpZ2h0X21lYW5fYnlfc3BlY2llcyAlPiUga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJjb25kZW5zZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImxlZnQiKQoKc3VydmV5c193ZWlnaHRfaW1wdXRlZCA8LSBzdXJ2ZXlzX2NvbWJpbmVkX3llYXIgJT4lIGdyb3VwX2J5KHNwZWNpZXNfaWQpICU+JSBtdXRhdGUod2VpZ2h0ID0gaWZlbHNlKGlzLm5hKHdlaWdodCksIG1lYW4od2VpZ2h0LCBuYS5ybSA9IFRSVUUpLCB3ZWlnaHQpKSAjIE1lYW4gYnkgc3BlY2llcyB0eXBlIGltcHV0YXRpb24gZm9yIHdlaWdodCBjb2x1bW4KCiNzdXJ2ZXlzX3dlaWdodF9pbXB1dGVkIDwtIGxlZnRfam9pbihzdXJ2ZXlzX2NvbWJpbmVkX3llYXIsIHdlaWdodF9tZWFuX2J5X3NwZWNpZXMsIGJ5PSAic3BlY2llc19pZCIpICU+JSBtdXRhdGUod2VpZ2h0ID0gaWZlbHNlKGlzLm5hKHdlaWdodC54KSwgd2VpZ2h0LnksIHdlaWdodC54KSkgJT4lIHNlbGVjdCgtd2VpZ2h0LnksIC13ZWlnaHQueCkgIyBhZGRpbmcgd2VpZ2h0X21lYW5fYnlfc3BlY2llcyBkYXRhc2V0IHRvIHN1cnZleXNfY29tYmluZWRfeWVhciBkYXRhc2V0IHRvIGZpbGwgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSB3ZWlnaHQgY29sdW1uCmhlYWQoc3VydmV5c193ZWlnaHRfaW1wdXRlZCkKVmlldyhzdXJ2ZXlzX3dlaWdodF9pbXB1dGVkKQpgYGAKCioqQ2hlY2tpbmcgSW1wdXRhdGlvbioqCkFzIHdlIGhhdmUgcmVwbGFjZWQgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSDigJx3ZWlnaHTigJ0gY29sdW1uIHdpdGggdGhlIG1lYW4gdmFsdWVzIGJ5IHNwZWNpZXMgdHlwZSwgdGhlIGNvdW50IG9mIHRvdGFsIE5BIHZhbHVlcyBpbiAqKnN1cnZleXNfd2VpZ2h0X2ltcHV0ZWQqKiBkZidzIHdlaWdodCBjb2x1bW4gc2hvdWxkIGJlIGxlc3MgdGhhbiB0aGF0IHZhbHVlIG9mICoqc3VydmV5c19jb21iaW5lZF95ZWFyKiouIEhvd2V2ZXIsIGl0IGNhbiBiZSBub3RpY2VkIHRoYXQgdGhlIE5BIGNvdW50IGlzIG5vbiB6ZXJvIGluIHRoZSB3ZWlnaHQgY29sdW1uIG9mICoqc3VydmV5c193ZWlnaHRfaW1wdXRlZCoqIGRhdGEgZnJhbWUuIFRoaXMgaXNzdWUgd2lsbCBiZSBkaXNjdXNzZWQgaW4gdGhlIG5leHQgdGFzay4KCmBgYHtyfQpzdW0oaXMubmEoc3VydmV5c19jb21iaW5lZF95ZWFyJHdlaWdodCkpCnN1bShpcy5uYShzdXJ2ZXlzX3dlaWdodF9pbXB1dGVkJHdlaWdodCkpCmBgYAoKCgojIyBUYXNrIDk6IFNwZWNpYWwgVmFsdWVzCgpJbnNwZWN0aW5nIHRoZSBgd2VpZ2h0YCBjb2x1bW4gaW4gYHN1cnZleXNfd2VpZ2h0X2ltcHV0ZWRgIGRhdGFmcmFtZSBmb3IgYW55IHNwZWNpYWwgdmFsdWVzIChpLmUuLCBOYU4sIEluZiwgLUluZikuIAoKYGBge3J9CiMgVGFzayA5CgpjYXQoIm1pc3NpbmcgdmFsdWUgY291bnQgaW4gd2VpZ2h0cyBvZiBzdXJ2ZXlzX2NvbWJpbmVkX3llYXI6ICIsIHN1bShpcy5uYShzdXJ2ZXlzX2NvbWJpbmVkX3llYXIkd2VpZ2h0KSkpCgpjYXQoIlxuU3BlY2lhbCB2YWx1ZSBjb3VudCBpbiB3ZWlnaHRzIG9mIHN1cnZleXNfd2VpZ2h0X2ltcHV0ZWQ6ICIsc3VtKGlzLm5hbihzdXJ2ZXlzX3dlaWdodF9pbXB1dGVkJHdlaWdodCkpKQoKYGBgCkl0IGNhbiBiZSBub3RpY2VkIHRoYXQgc29tZSBvZiB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIHJlY29yZGVkIGFzIHNwZWNpYWwgdmFsdWVzLCAiTkFOIiB3aGljaCBzdGFuZHMgZm9yICJOb3QgQSBOdW1iZXIiIiBhbmQgaXMgYSBsb2dpY2FsIHZlY3RvciBvZiBhIGxlbmd0aCAxIGFuZCBhcHBsaWVzIHRvIG51bWVyaWNhbCB2YWx1ZXMuIEluIHBhcnQgOCB3aGVuIHdlIGNhbGN1bGF0ZWQgdGhlIG1lYW4gdXNpbmcgYWdncmlnYXRlKCkgZnVuY3Rpb24gYnkgcGFzc2luZyBhbiBhcmd1bWVudCBuYS5ybT1UUlVFLCB3aGljaCBpZ25vcmVzIG1pc3NpbmcgdmFsdWVzLiBUaGVyZSBhcmUgZ3JvdXBzIGluIHN1cnZleXNfY29tYmluZWRfeWVhciBpbiB3aGljaCBhbGwgdmFsdWVzIGluIHdlaWdodCBjb2x1bW4gYXJlIG1pc3NpbmcgdmFsdWVzLiBUaGUgZ3JvdXBzIGluIG91ciBjYXNlIGFyZSAo4oCcQULigJ0sIOKAnEFI4oCdLOKAnENC4oCdLOKAnENT4oCdKS4gV2hlbiBhcHBseWluZyB0aGUgZnVuY3Rpb24gbWVhbiguLi4uLG5hLnJtPVRSVUUpIG9uIHRoZSBmb3JlIG1lbnRpb25lZCBzcGVjaWVzX2lkJ3Mgd2VpZ2h0IGNvbHVtbiwgYXMgYWxsIHZhbHVlcyBhcmUgTkEncywgaXQgd2lsbCBzZWxlY3QgemVybyB2YWx1ZXMgYW5kIGRpdmlkZSBpdCBieSB0b3RhbCB2YWx1ZXMgc2VsZWN0ZWQoMCksIHdoaWNoIHdpbGwgcmV0dXJuICJOQU4iLgoKCiMjIFRhc2sgMTA6IE91dGxpZXJzCgpJbiB0aGUgYHN1cnZleXNfY29tYmluZWRgIGRhdGEgZnJhbWUsIHRoZSB2YXJpYWJsZSBoaW5kZm9vdCBsZW5ndGggd2FzIGluc3BlY3RlZCBmb3IgcG9zc2libGUgdW5pdmFyaWF0ZSBvdXRsaWVycy4gCkRpZmZlcmVudCBhcHByb2FjaGVzIGNhbiBiZSB1c2VkIHRvIGRldGVjdCB1bml2YXJpYXRlIG91dGxpZXJzIHN1Y2ggYXM7CiAtIFR1a2V54oCZcyBtZXRob2Qgb2Ygb3V0bGllciBkZXRlY3Rpb24KIC0gQm94IHBsb3RzCiAtIHotc2NvcmUgbWV0aG9kCiAKRmlyc3QgYSBib3hwbG90IHdhcyBjb25zdHJ1Y3RlZCB0byB2aXN1YWxseSBpZGVudGlmeSB3aGV0aGVyIHRoZXJlIGFyZSBwb3NzaWJsZSBvdXRsaWVycyBpbiB0aGUgZGF0YSBzZXQuIFZlcnkgZmV3IG51bWJlciBvZiBvdXRsaWVycyB3ZXJlIHZpc2libGUgaW4gdGhlIGJveHBsb3QuIEluIGFkZGl0aW9uLCB0aGUgYm94cGxvdCBkZXBpY3RlZCBhIHNrZXduZXNzIGluIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGhpbmRmb290X2xlbmd0aCB2YXJpYWJsZSwgd2hpY2ggZG9lcyBub3Qgc3VwcG9ydCB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24uIEFuZCBoZW5jZSBmb3IgdGhlIG91dGxpZXIgZGV0ZWN0aW9uIHRhc2ssIHRoZSB0d28gbm9uIHBhcmFtZXRyaWMgbWV0aG9kcywgVHVrZXkncyBtZXRob2QgYW5kIGJveHBsb3Qgd2VyZSB1c2VkIGFzIHRoZXkgZG9uJ3QgcmVxdWlyZSBhbnkgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIGRpc3RyaWJ1dGlvbi4KYGBge3J9Ck91dFZhbHMgPSBib3hwbG90KHN1cnZleXNfY29tYmluZWQkaGluZGZvb3RfbGVuZ3RoLG1haW49IkJveHBsb3QgZm9yIGhpbmRmb290X2xlbmd0aCIsIHlsYWI9IkhpbmRmb290IExlbmd0aCIsIGNvbCA9InNlYWdyZWVuIikkb3V0CndoaWNoKHN1cnZleXNfY29tYmluZWQkaGluZGZvb3RfbGVuZ3RoICVpbiUgT3V0VmFscykKQm94cGxvdChzdXJ2ZXlzX2NvbWJpbmVkJGhpbmRmb290X2xlbmd0aCxpZD1UUlVFLCB5bGFiPSJIaW5kZm9vdCBMZW5ndGgiLCBjb2wgPSJzZWFncmVlbiIpI2JveHBsb3Qgd2l0aCBsb2NhdGlvbiBvZiB0aGUgb3V0bGllcnMKCmBgYAoKSXQgaXMgdmlzaWJsZSBmcm9tIHRoZSBCb3hwbG90IHRoYXQgMiBvYnNlcnZhdGlvbnMgYXJlIG91dHNpZGUgb2YgdGhlIHRvcCB3aXNrZXIgd2hpY2ggZGVub3RlcyAoUTMrMS41eElRUikuIEhlbmNlLCBxdWFsaWZpZWQgdG8gYmUgY29uc2lkZXJlZCBhcyAqKm91dGxpZXJzKiogYWNjb3JkaW5nIHRvIFR1a2V5J3MgTWV0aG9kLiAKCioqSGFuZGVsaW5nIE91dGxpZXJzKioKCk9ubHkgdHdvIG91dGxpZXJzIHdlcmUgZGV0ZWN0ZWQgYW5kIGl0IGlzIGEgdmVyeSBzbWFsbCBhbW91bnQgd2hlbiBjb21wYXJlZCB0byB0aGUgY29tcGxldGUgZGF0YXNldCBzdXJ2ZXlzX2NvbWJpbmVkIHdoaWNoIGhhcyBvdmVyIDMxLDAwMCBvYnNlcnZhdGlvbnMuIFRodXMgdGhlIGVmZmVjdCBvZiB0aGUgb3V0bGllciBvbiB0aGUgZGF0YSBkaXN0cmlidXRpb24gcGFyYW1ldGVycyB3aWxsIGJlIG5lZ2xpZ2libGUuIEhlbmNlLCBleGNsdWRpbmcgb3V0bGllcnMgd2lsbCBiZSB0aGUgYmVzdCBzdWl0ZWQgYXBwcm9hY2ggdG8gaGFuZGxlIG91dGxpZXJzIGluIGRhdGFzZXQgc3VydmV5c19jb21iaW5lZC4KCmBgYHtyfQojIFRhc2sgMTAuIAoKc3VtbWFyeShzdXJ2ZXlzX2NvbWJpbmVkJGhpbmRmb290X2xlbmd0aCkgIyBzdW1tYXJ5IHN0YXRpc3RpY3MgYmVmb3JlIHJlbW92aW5nIG91dGxpZXJzCgpzdXJ2ZXlzX2NvbWJpbmVkIDwtIHN1cnZleXNfY29tYmluZWRbLWMoMTA1NzQsMzA0MjUpLF0gI0V4Y2x1ZGluZyBvdXRsaWVycyhyb3dzKSBmcm9tIHRoZSBkYXRhZnJhbWUKc3VtbWFyeShzdXJ2ZXlzX2NvbWJpbmVkJGhpbmRmb290X2xlbmd0aCkgIyBzdW1tYXJ5IHN0YXRpc3RpY3MgYWZ0ZXIgcmVtb3Zpbmcgb3V0bGllcnMKCnN1cnZleXNfY29tYmluZWQkaGluZGZvb3RfbGVuZ3RoICU+JSAgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBIaW5kZm9vdCBMZW5ndGgiLCB5bGFiPSJIaW5kZm9vdCBMZW5ndGgiLCBjb2wgPSAibGlnaHRibHVlIikgIyBjaGVja2luZyB0aGUgaW1wdXRlZCBkYXRhc2V0CgpgYGAKCgoKCjxicj4KPGJyPgo=