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 = [31mcol_character()[39m,
iso2 = [31mcol_character()[39m,
iso3 = [31mcol_character()[39m
)
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 = [31mcol_character()[39m,
genus = [31mcol_character()[39m,
species = [31mcol_character()[39m,
taxa = [31mcol_character()[39m
)
head(species)
surveys <- read_csv("surveys.csv")
Parsed with column specification:
cols(
record_id = [32mcol_double()[39m,
month = [32mcol_double()[39m,
day = [32mcol_double()[39m,
year = [32mcol_double()[39m,
species_id = [31mcol_character()[39m,
sex = [31mcol_character()[39m,
hindfoot_length = [32mcol_double()[39m,
weight = [32mcol_double()[39m
)
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=