Required packages

library(readr)
library(dplyr)
library(tidyr)
library(Hmisc)
library(stringr)
library(lubridate)
library(outliers)
library(MVN)
library(ggplot2)
library(knitr)
library(gdata)
library(mlr)
library(forecast)

Executive Summary

Data Preprocessing plays an important role in Statistical Analysis of data as it impacts the results based on data.Data Preprocessing is a technique of transforming raw data into a understandable format.We have performed all the necessary steps required like get,understand, tidy,manipulate , scan & transform.

At the first , we imported the two datasets. Both the datasets were sorted and a new dataset was formed by using “Inner Join” function.This newly formed subset is understood in terms of its class & structure and for some variables were converted into factors.

Then the data was checked for its tidyness and for some variables ,rules were applied to seperate alpha-numeric variables. ‘dplyr’ package was also explored for setting the carrier years of a player using mutate () function by evaluating end_year-start_year. For checking the missing values if any , is.na function was used and the missing values were imputed using median value for the particular variable.A new subset was created with 2 numeric and 1 character variable and it was scanned using Turkey’s fencing method and outliers were detected using z-score and for those variables showing outliers were cleaned by capping function.

Data

The dataset used for this project was about an ‘American Football League’. It is one of prime sports played in North America .The data was taken from kaggle.com’, an open data source software.The link for the dataset is:https://www.kaggle.com/kendallgillies/nflstatistics#Basic_Stats.csv

There were 2 datasets choosen for this assignment namely- 1).Basic.csv which contained statistics of the players.The dataset contained variables as follows:Age, Birthday,Birthplace , Height, Current Status, College Attended,etc

2).Game.csv which conatined statistics of Game Logs for each player.Each player have some specific roles in some areas. We had choosen Game statistics of player on the offensive line and stored it in ‘Game.csv’ file.It contained following variables namely :Player_id, Name, Position, Year,Team, Games Played , Season,etc.

The working directory was first checked and then set with directory where the data files were present.The file was imported using read_csv function which is similar as read.csv but is efficient and faster.

getwd()
[1] "G:/Semester-2/Data Pre-processing"
setwd("G:/Semester-2/Data Pre-processing/Assignment")
The working directory was changed to G:/Semester-2/Data Pre-processing/Assignment inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
play<- read_csv("Basic.csv")
Parsed with column specification:
cols(
  Age = col_double(),
  `Birth Place` = col_character(),
  Birthday = col_character(),
  College = col_character(),
  `Current Status` = col_character(),
  `Current Team` = col_character(),
  Experience = col_character(),
  `Height (inches)` = col_double(),
  `High School` = col_character(),
  `High School Location` = col_character(),
  Name = col_character(),
  Number = col_double(),
  `Player Id` = col_character(),
  Position = col_character(),
  `Weight (lbs)` = col_double(),
  `Years Played` = col_character()
)
head(play)

Using head function , first few rows of the data are displayed.

game<- read_csv("Game.csv")
Parsed with column specification:
cols(
  `Player Id` = col_character(),
  Name = col_character(),
  Position = col_character(),
  Year = col_double(),
  Season = col_character(),
  Week = col_double(),
  `Game Date` = col_character(),
  `Home or Away` = col_character(),
  Opponent = col_character(),
  Outcome = col_character(),
  Score = col_character(),
  `Games Played` = col_double(),
  `Games Started` = col_character()
)
head(game)

The dataset below is subset of original dataset in which 7 variables were taken from 16 variables . The varibles choosen were : PlayerId,Experience, Weight, Height, Birthday, College Attendend, Current Status.

play_new <- play[,c(13,3,4,5,7,8,15)]
head(play_new)

Now the columns of second dataset i.e Game.csv were sorted & changed to improve the quality of dataset.From the 13 variables of dataset , 2 variables were formed after applying summarise function and were grouped by ‘Player_ID’

game_new <- game %>% group_by(`Player Id`) %>% summarise(start_year=min(Year,na.rm=FALSE),end_year=max(Year,na.rm = FALSE),                                              total_games=sum(`Games Played`,na.rm=FALSE))
head(game_new)

The subsets of the main data (play_new & game_new ) were joined together and named as football using inner_join function on variable player_ID.

football <- play_new %>% inner_join(game_new,by="Player Id")
head(football)

Understand

firstly , class function was used to check the class/type.

class(football$`Height (inches)`)
[1] "numeric"

The class of Height varible is seen to be numeric.Hence, to convert it to integer format we have used as.integer function.

football$`Height (inches)`<-as.integer(football$`Height (inches)`)
class(football$`Height (inches)`)
[1] "integer"
class(football$Birthday)
[1] "character"

Next the class for variable Birthday was shown as character as the reason being date & time values are imported as character value in R by default. Therefore ,we converted it into suitable date-time form.

football$Birthday <- mdy(football$Birthday)
class(football$Birthday)
[1] "Date"

Finally, the Variable ‘Current Status’ is converted to factor variable and the variable ‘Experience’ is sorted , factored ,labelled and ordered as per experinec of players.

class(football$`Current Status`)
[1] "character"
football$`Current Status`<-as.factor(football$`Current Status`)
class(football$`Current Status`)
[1] "factor"
football$Experience <- factor( c(football$Experience), 
                              levels = c("0 Season", "1 Season", "1st season",   "2 Seasons",
                                         "2nd season",   "3 Seasons",    "3rd season",   "4 Seasons", 
                                         "4th season",   "5 Seasons",    "5th season",   "6 Seasons", 
                                         "6th season",   "7 Seasons",    "7th season",   "8 Seasons",
                                         "8th season",   "9 Seasons",    "9th season",   "10 Seasons",
                                         "10th season",  "11 Seasons",   "11th season",  "12 Seasons",
                                         "13 Seasons",   "14 Seasons",   "14th season",  "17 Seasons",
                                         "18 Seasons",   "18th season"), 
                              labels = c("0 Season", "1 Season", "1 Season", "2 Seasons", 
                                         "2 Seasons",    "3 Seasons",    "3 Seasons",    "4 Seasons",
                                         "4 Seasons",    "5 Seasons",    "5 Seasons",    "6 Seasons",
                                         "6 Seasons",    "7 Seasons",    "7 Seasons",    "8 Seasons",
                                         "8 Seasons",    "9 Seasons",    "9 Seasons",    "10 Seasons",
                                         "10 Seasons",   "11 Seasons",   "11 Seasons",   "12 Seasons",  
                                         "13 Seasons",   "14 Seasons",   "14 Seasons",   "17 Seasons",
                                         "18 Seasons",   "18 Seasons"), ordered = TRUE)

Tidy & Manipulate Data I

As per statistican Hardley Wickham and Grolemund ,there are 3 tidy rule as: 1) Each variable in the dataset is placed in its own column. 2) Each observation is placed in its own row. 3) Each value is placed in its own cell. and the data that satisfies these 3 rules is known as tidy data.

In our original dataset, their was 1 variable namely playerID which was a combination of firstname,lastname and player_ID.As we were not confirmed about the firstname and familyname, we decided to seperate the variable on basis of ID. The below code shows how player_ID is seperated into Name and ID by using seperate() of tidyr package.

football_1 <-football %>% separate (`Player Id`,into= c("Names","ID"),sep='/')
head(football_1)

Tidy & Manipulate Data II

Mutate() function is used for creation of a new column called career_years. By looking at the data we see that some players have started playing and given up in same year.But when we take the difference of their start_year & end_year we get 0 which is logically incorrect.To solve the issue we have added 1 explicity .

mutate(football_1,career_years=end_year-start_year+1)

Scan I

The numeric variables are further subsetted into football_subset with variables ID, Height & Weight to scan any missing values/inconsistences.

football_subset<- football_1[,c(2,7,8)]
football_subset

To calculate the total number of missing values in each column , we used colsum function. We found 1 missing value in Height column. which() function is then applied to find the location of NA in that variable

colSums(is.na(football_subset))
             ID Height (inches)    Weight (lbs) 
              0               1               0 
which(is.na(football_subset$`Height (inches)`))
[1] 253

As their are many ways to handle the missing values in dataset such as replacing missing values with constants or a mean or median values of the that variable ,here we have imputed the missing value with ’Median Value’of height column.After imputing the values, the variable is checked again to see if changes are applied.

football_subset$`Height (inches)`[is.na(football_subset$`Height (inches)`)] <-median(football_subset$`Height (inches)`,na.rm = TRUE)
colSums(is.na(football_subset))
             ID Height (inches)    Weight (lbs) 
              0               0               0 

Scan II

We know methods for detecting outliers are distance based method and Turkey’s method of outlier detection.We therefore created box plot using Turkey’s method and outliers are seen.

football_subset$`Height (inches)` %>% boxplot(main="Boxplot for Height",ylab="Height In Inches")

A box plot is standardised way of displaying distribution of data based on summary statistics and shows the outliers and what their values are. The distance based (Z-score) helps in confirming the number of outliers detected. We then calculated the z-score for height variable .Also we know that an observation is said to be an outlier if the absolute value of its z-score is > 3.Hence, according to z-score the value is 1 which is computed using length function.

zscores_football_height <-football_subset$`Height (inches)` %>% scores(type="z")
zscores_football_height %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.8395 -0.7722 -0.1588  0.0000  0.4547  2.9085 
length (which( abs(zscores_football_height) >3 ))
[1] 1

Their are multiple ways to take care of outliers like excluding ,imputing , capping & binning. But before we use any of the methods mentioned above , we need to analysed it properly .Due to insufficient reasons to measure the certaninty of outliers we then used user-defined capping method.

cap <- function(x){
  quantiles <- quantile( x, c(.05, 0.25, 0.75, .95 ) )
  x[ x < quantiles[2] - 1.5*IQR(x) ] <- quantiles[1]
  x[ x > quantiles[3] + 1.5*IQR(x) ] <- quantiles[4]
  x
}
football_height_capped <- football_subset$`Height (inches)` %>% cap()

To cross check if any outliers are present , we plotted a boxplot for football_height_capped.

football_height_capped %>% boxplot(main="Boxplot for Height",ylab="Height In Inches")

Transform

Before the data is used to prepare a model , we applied some transformations on the tidy data. Data Transformation is used in many situations like - 1) To alter the scale of variable 2) To transform non-linear into linear relationship 3) To reduce skewness for making it a normal distribution

The distribution of height variable is then visualised using a histogram. The histogram appears to be normally distributed.Therefore we did not applied any further transforamtion .

hist(football_height_capped)



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDIsIDIwMTkiDQphdXRob3I6ICJQb29ydmFzaGkgUG9raHVuIFMzMzkwNjAyICAmIEFua2l0IE11bm90IHMzNzY0OTUwIg0Kc3VidGl0bGU6IEFzc2lnbm1lbnQgMw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0KIyMgUmVxdWlyZWQgcGFja2FnZXMgDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShIbWlzYykNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShvdXRsaWVycykNCmxpYnJhcnkoTVZOKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoZ2RhdGEpDQpsaWJyYXJ5KG1scikNCmxpYnJhcnkoZm9yZWNhc3QpDQpgYGANCg0KDQojIyBFeGVjdXRpdmUgU3VtbWFyeSANCkRhdGEgUHJlcHJvY2Vzc2luZyBwbGF5cyBhbiBpbXBvcnRhbnQgcm9sZSBpbiBTdGF0aXN0aWNhbCBBbmFseXNpcyBvZiBkYXRhIGFzIGl0IGltcGFjdHMgdGhlIHJlc3VsdHMgYmFzZWQgb24gZGF0YS5EYXRhIFByZXByb2Nlc3NpbmcgaXMgYSB0ZWNobmlxdWUgb2YgdHJhbnNmb3JtaW5nIHJhdyBkYXRhIGludG8gYSB1bmRlcnN0YW5kYWJsZSBmb3JtYXQuV2UgaGF2ZSBwZXJmb3JtZWQgYWxsIHRoZSBuZWNlc3Nhcnkgc3RlcHMgcmVxdWlyZWQgbGlrZSBnZXQsdW5kZXJzdGFuZCwgdGlkeSxtYW5pcHVsYXRlICwgc2NhbiAmIHRyYW5zZm9ybS4NCg0KQXQgdGhlIGZpcnN0ICwgd2UgaW1wb3J0ZWQgdGhlIHR3byBkYXRhc2V0cy4gQm90aCB0aGUgZGF0YXNldHMgd2VyZSBzb3J0ZWQgYW5kIGEgbmV3IGRhdGFzZXQgd2FzIGZvcm1lZCBieSB1c2luZyAiSW5uZXIgSm9pbiIgZnVuY3Rpb24uVGhpcyBuZXdseSBmb3JtZWQgc3Vic2V0IGlzIHVuZGVyc3Rvb2QgaW4gdGVybXMgb2YgaXRzIGNsYXNzICAmIHN0cnVjdHVyZSBhbmQgZm9yIHNvbWUgdmFyaWFibGVzIHdlcmUgY29udmVydGVkICBpbnRvIGZhY3RvcnMuDQoNClRoZW4gdGhlIGRhdGEgd2FzIGNoZWNrZWQgZm9yIGl0cyB0aWR5bmVzcyBhbmQgZm9yIHNvbWUgdmFyaWFibGVzICxydWxlcyB3ZXJlIGFwcGxpZWQgdG8gc2VwZXJhdGUgYWxwaGEtbnVtZXJpYyB2YXJpYWJsZXMuICdkcGx5cicgcGFja2FnZSB3YXMgYWxzbyBleHBsb3JlZCBmb3Igc2V0dGluZyB0aGUgY2FycmllciB5ZWFycyBvZiBhIHBsYXllciB1c2luZyBtdXRhdGUgKCkgZnVuY3Rpb24gYnkgZXZhbHVhdGluZyBlbmRfeWVhci1zdGFydF95ZWFyLg0KRm9yIGNoZWNraW5nIHRoZSBtaXNzaW5nIHZhbHVlcyBpZiBhbnkgLCBpcy5uYSBmdW5jdGlvbiB3YXMgdXNlZCBhbmQgdGhlIG1pc3NpbmcgdmFsdWVzIHdlcmUgaW1wdXRlZCB1c2luZyBtZWRpYW4gdmFsdWUgZm9yIHRoZSBwYXJ0aWN1bGFyIHZhcmlhYmxlLkEgbmV3IHN1YnNldCB3YXMgY3JlYXRlZCB3aXRoIDIgbnVtZXJpYyBhbmQgMSBjaGFyYWN0ZXIgdmFyaWFibGUgYW5kIGl0IHdhcyBzY2FubmVkIHVzaW5nIFR1cmtleSdzIGZlbmNpbmcgbWV0aG9kIGFuZCBvdXRsaWVycyB3ZXJlIGRldGVjdGVkIHVzaW5nIHotc2NvcmUgYW5kIGZvciB0aG9zZSB2YXJpYWJsZXMgc2hvd2luZyBvdXRsaWVycyB3ZXJlIGNsZWFuZWQgYnkgY2FwcGluZyBmdW5jdGlvbi4NCg0KDQojIyBEYXRhIA0KVGhlIGRhdGFzZXQgdXNlZCBmb3IgdGhpcyBwcm9qZWN0IHdhcyBhYm91dCBhbiAnQW1lcmljYW4gRm9vdGJhbGwgTGVhZ3VlJy4gSXQgaXMgb25lIG9mIHByaW1lIHNwb3J0cyBwbGF5ZWQgaW4gTm9ydGggQW1lcmljYSAuVGhlIGRhdGEgd2FzIHRha2VuIGZyb20ga2FnZ2xlLmNvbScsIGFuIG9wZW4gZGF0YSBzb3VyY2Ugc29mdHdhcmUuVGhlIGxpbmsgZm9yIHRoZSBkYXRhc2V0IGlzOmh0dHBzOi8vd3d3LmthZ2dsZS5jb20va2VuZGFsbGdpbGxpZXMvbmZsc3RhdGlzdGljcyNCYXNpY19TdGF0cy5jc3YNCg0KVGhlcmUgd2VyZSAyIGRhdGFzZXRzIGNob29zZW4gZm9yIHRoaXMgYXNzaWdubWVudCBuYW1lbHktDQoxKS5CYXNpYy5jc3Ygd2hpY2ggY29udGFpbmVkIHN0YXRpc3RpY3Mgb2YgdGhlIHBsYXllcnMuVGhlIGRhdGFzZXQgY29udGFpbmVkIHZhcmlhYmxlcyBhcyBmb2xsb3dzOkFnZSwgQmlydGhkYXksQmlydGhwbGFjZSAsIEhlaWdodCwgQ3VycmVudCBTdGF0dXMsIENvbGxlZ2UgQXR0ZW5kZWQsZXRjDQoNCjIpLkdhbWUuY3N2IHdoaWNoIGNvbmF0aW5lZCBzdGF0aXN0aWNzIG9mIEdhbWUgTG9ncyBmb3IgZWFjaCBwbGF5ZXIuRWFjaCBwbGF5ZXIgaGF2ZSBzb21lIHNwZWNpZmljIHJvbGVzIGluIHNvbWUgYXJlYXMuIFdlIGhhZCBjaG9vc2VuIEdhbWUgc3RhdGlzdGljcyBvZiBwbGF5ZXIgb24gdGhlIG9mZmVuc2l2ZSBsaW5lIGFuZCBzdG9yZWQgaXQgaW4gJ0dhbWUuY3N2JyBmaWxlLkl0IGNvbnRhaW5lZCBmb2xsb3dpbmcgdmFyaWFibGVzIG5hbWVseSA6UGxheWVyX2lkLCBOYW1lLCBQb3NpdGlvbiwgWWVhcixUZWFtLCBHYW1lcyBQbGF5ZWQgLCBTZWFzb24sZXRjLg0KDQpUaGUgd29ya2luZyBkaXJlY3Rvcnkgd2FzIGZpcnN0IGNoZWNrZWQgYW5kIHRoZW4gc2V0IHdpdGggZGlyZWN0b3J5IHdoZXJlIHRoZSBkYXRhIGZpbGVzIHdlcmUgcHJlc2VudC5UaGUgZmlsZSB3YXMgaW1wb3J0ZWQgdXNpbmcgcmVhZF9jc3YgZnVuY3Rpb24gd2hpY2ggaXMgc2ltaWxhciBhcyByZWFkLmNzdiBidXQgaXMgZWZmaWNpZW50IGFuZCBmYXN0ZXIuDQoNCg0KYGBge3J9DQoNCmdldHdkKCkNCnNldHdkKCJHOi9TZW1lc3Rlci0yL0RhdGEgUHJlLXByb2Nlc3NpbmcvQXNzaWdubWVudCIpDQoNCnBsYXk8LSByZWFkX2NzdigiQmFzaWMuY3N2IikNCg0KaGVhZChwbGF5KQ0KYGBgDQpVc2luZyBoZWFkIGZ1bmN0aW9uICwgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIGRhdGEgYXJlIGRpc3BsYXllZC4NCg0KYGBge3J9DQpnYW1lPC0gcmVhZF9jc3YoIkdhbWUuY3N2IikNCmhlYWQoZ2FtZSkNCg0KYGBgDQpUaGUgZGF0YXNldCBiZWxvdyBpcyBzdWJzZXQgb2Ygb3JpZ2luYWwgZGF0YXNldCBpbiB3aGljaCA3IHZhcmlhYmxlcyB3ZXJlIHRha2VuIGZyb20gMTYgdmFyaWFibGVzIC4gVGhlIHZhcmlibGVzIGNob29zZW4gd2VyZSA6IFBsYXllcklkLEV4cGVyaWVuY2UsIFdlaWdodCwgSGVpZ2h0LCBCaXJ0aGRheSwgQ29sbGVnZSBBdHRlbmRlbmQsIEN1cnJlbnQgU3RhdHVzLg0KDQpgYGB7cn0NCg0KcGxheV9uZXcgPC0gcGxheVssYygxMywzLDQsNSw3LDgsMTUpXQ0KDQpoZWFkKHBsYXlfbmV3KQ0KDQpgYGANCg0KTm93IHRoZSBjb2x1bW5zIG9mIHNlY29uZCBkYXRhc2V0IGkuZSBHYW1lLmNzdiB3ZXJlIHNvcnRlZCAmIGNoYW5nZWQgdG8gaW1wcm92ZSB0aGUgcXVhbGl0eSBvZiBkYXRhc2V0LkZyb20gdGhlIDEzIHZhcmlhYmxlcyBvZiBkYXRhc2V0ICwgMiB2YXJpYWJsZXMgd2VyZSBmb3JtZWQgYWZ0ZXIgYXBwbHlpbmcgc3VtbWFyaXNlIGZ1bmN0aW9uIGFuZCB3ZXJlIGdyb3VwZWQgYnkgJ1BsYXllcl9JRCcNCg0KYGBge3J9DQoNCmdhbWVfbmV3IDwtIGdhbWUgJT4lIGdyb3VwX2J5KGBQbGF5ZXIgSWRgKSAlPiUgc3VtbWFyaXNlKHN0YXJ0X3llYXI9bWluKFllYXIsbmEucm09RkFMU0UpLGVuZF95ZWFyPW1heChZZWFyLG5hLnJtID0gRkFMU0UpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9nYW1lcz1zdW0oYEdhbWVzIFBsYXllZGAsbmEucm09RkFMU0UpKQ0KDQoNCmhlYWQoZ2FtZV9uZXcpDQoNCmBgYA0KDQpUaGUgc3Vic2V0cyBvZiB0aGUgbWFpbiBkYXRhIChwbGF5X25ldyAmIGdhbWVfbmV3ICkgd2VyZSBqb2luZWQgdG9nZXRoZXIgYW5kIG5hbWVkIGFzIGZvb3RiYWxsIHVzaW5nIGlubmVyX2pvaW4gZnVuY3Rpb24gb24gdmFyaWFibGUgcGxheWVyX0lELg0KDQpgYGB7cn0NCg0KZm9vdGJhbGwgPC0gcGxheV9uZXcgJT4lIGlubmVyX2pvaW4oZ2FtZV9uZXcsYnk9IlBsYXllciBJZCIpDQoNCmhlYWQoZm9vdGJhbGwpDQoNCmBgYA0KDQoNCiMjIFVuZGVyc3RhbmQgDQogZmlyc3RseSAsIGNsYXNzIGZ1bmN0aW9uIHdhcyB1c2VkIHRvIGNoZWNrIHRoZSBjbGFzcy90eXBlLg0KDQpgYGB7cn0NCg0KY2xhc3MoZm9vdGJhbGwkYEhlaWdodCAoaW5jaGVzKWApDQoNCmBgYA0KVGhlIGNsYXNzIG9mIEhlaWdodCB2YXJpYmxlIGlzIHNlZW4gdG8gYmUgbnVtZXJpYy5IZW5jZSwgdG8gY29udmVydCBpdCB0byBpbnRlZ2VyIGZvcm1hdCB3ZSBoYXZlIHVzZWQgYXMuaW50ZWdlciBmdW5jdGlvbi4NCg0KYGBge3J9DQoNCmZvb3RiYWxsJGBIZWlnaHQgKGluY2hlcylgPC1hcy5pbnRlZ2VyKGZvb3RiYWxsJGBIZWlnaHQgKGluY2hlcylgKQ0KY2xhc3MoZm9vdGJhbGwkYEhlaWdodCAoaW5jaGVzKWApDQoNCmNsYXNzKGZvb3RiYWxsJEJpcnRoZGF5KQ0KDQpgYGANCg0KTmV4dCB0aGUgY2xhc3MgZm9yIHZhcmlhYmxlIEJpcnRoZGF5IHdhcyBzaG93biBhcyBjaGFyYWN0ZXIgYXMgdGhlIHJlYXNvbiBiZWluZyBkYXRlICYgdGltZSB2YWx1ZXMgYXJlIGltcG9ydGVkIGFzIGNoYXJhY3RlciB2YWx1ZSBpbiBSIGJ5IGRlZmF1bHQuIFRoZXJlZm9yZSAsd2UgY29udmVydGVkIGl0IGludG8gc3VpdGFibGUgZGF0ZS10aW1lIGZvcm0uIA0KDQpgYGB7cn0NCmZvb3RiYWxsJEJpcnRoZGF5IDwtIG1keShmb290YmFsbCRCaXJ0aGRheSkNCmNsYXNzKGZvb3RiYWxsJEJpcnRoZGF5KQ0KDQpgYGANCg0KRmluYWxseSwgdGhlICBWYXJpYWJsZSAnQ3VycmVudCBTdGF0dXMnIGlzIGNvbnZlcnRlZCB0byBmYWN0b3IgdmFyaWFibGUgYW5kIHRoZSB2YXJpYWJsZSAnRXhwZXJpZW5jZScgaXMgc29ydGVkICwgZmFjdG9yZWQgLGxhYmVsbGVkIGFuZCBvcmRlcmVkIGFzIHBlciBleHBlcmluZWMgb2YgcGxheWVycy4NCg0KDQpgYGB7cn0NCmNsYXNzKGZvb3RiYWxsJGBDdXJyZW50IFN0YXR1c2ApDQoNCmZvb3RiYWxsJGBDdXJyZW50IFN0YXR1c2A8LWFzLmZhY3Rvcihmb290YmFsbCRgQ3VycmVudCBTdGF0dXNgKQ0KDQpjbGFzcyhmb290YmFsbCRgQ3VycmVudCBTdGF0dXNgKQ0KDQoNCmZvb3RiYWxsJEV4cGVyaWVuY2UgPC0gZmFjdG9yKCBjKGZvb3RiYWxsJEV4cGVyaWVuY2UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIjAgU2Vhc29uIiwgIjEgU2Vhc29uIiwgIjFzdCBzZWFzb24iLCAgICIyIFNlYXNvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMm5kIHNlYXNvbiIsICAgIjMgU2Vhc29ucyIsICAgICIzcmQgc2Vhc29uIiwgICAiNCBTZWFzb25zIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0dGggc2Vhc29uIiwgICAiNSBTZWFzb25zIiwgICAgIjV0aCBzZWFzb24iLCAgICI2IFNlYXNvbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjZ0aCBzZWFzb24iLCAgICI3IFNlYXNvbnMiLCAgICAiN3RoIHNlYXNvbiIsICAgIjggU2Vhc29ucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI4dGggc2Vhc29uIiwgICAiOSBTZWFzb25zIiwgICAgIjl0aCBzZWFzb24iLCAgICIxMCBTZWFzb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEwdGggc2Vhc29uIiwgICIxMSBTZWFzb25zIiwgICAiMTF0aCBzZWFzb24iLCAgIjEyIFNlYXNvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTMgU2Vhc29ucyIsICAgIjE0IFNlYXNvbnMiLCAgICIxNHRoIHNlYXNvbiIsICAiMTcgU2Vhc29ucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxOCBTZWFzb25zIiwgICAiMTh0aCBzZWFzb24iKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIFNlYXNvbiIsICIxIFNlYXNvbiIsICIxIFNlYXNvbiIsICIyIFNlYXNvbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIgU2Vhc29ucyIsICAgICIzIFNlYXNvbnMiLCAgICAiMyBTZWFzb25zIiwgICAgIjQgU2Vhc29ucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0IFNlYXNvbnMiLCAgICAiNSBTZWFzb25zIiwgICAgIjUgU2Vhc29ucyIsICAgICI2IFNlYXNvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNiBTZWFzb25zIiwgICAgIjcgU2Vhc29ucyIsICAgICI3IFNlYXNvbnMiLCAgICAiOCBTZWFzb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjggU2Vhc29ucyIsICAgICI5IFNlYXNvbnMiLCAgICAiOSBTZWFzb25zIiwgICAgIjEwIFNlYXNvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTAgU2Vhc29ucyIsICAgIjExIFNlYXNvbnMiLCAgICIxMSBTZWFzb25zIiwgICAiMTIgU2Vhc29ucyIsICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEzIFNlYXNvbnMiLCAgICIxNCBTZWFzb25zIiwgICAiMTQgU2Vhc29ucyIsICAgIjE3IFNlYXNvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTggU2Vhc29ucyIsICAgIjE4IFNlYXNvbnMiKSwgb3JkZXJlZCA9IFRSVUUpDQoNCmBgYA0KDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSANCkFzIHBlciBzdGF0aXN0aWNhbiBIYXJkbGV5IFdpY2toYW0gYW5kIEdyb2xlbXVuZCAsdGhlcmUgYXJlIDMgdGlkeSBydWxlIGFzOg0KMSkgRWFjaCB2YXJpYWJsZSBpbiB0aGUgZGF0YXNldCBpcyBwbGFjZWQgaW4gaXRzIG93biBjb2x1bW4uDQoyKSBFYWNoIG9ic2VydmF0aW9uIGlzIHBsYWNlZCBpbiBpdHMgb3duIHJvdy4NCjMpIEVhY2ggdmFsdWUgaXMgcGxhY2VkIGluIGl0cyBvd24gY2VsbC4NCmFuZCB0aGUgZGF0YSB0aGF0IHNhdGlzZmllcyB0aGVzZSAzIHJ1bGVzIGlzIGtub3duIGFzIHRpZHkgZGF0YS4NCg0KSW4gb3VyIG9yaWdpbmFsIGRhdGFzZXQsIHRoZWlyIHdhcyAxIHZhcmlhYmxlIG5hbWVseSBwbGF5ZXJJRCB3aGljaCB3YXMgYSBjb21iaW5hdGlvbiBvZiBmaXJzdG5hbWUsbGFzdG5hbWUgYW5kIHBsYXllcl9JRC5BcyB3ZSB3ZXJlIG5vdCBjb25maXJtZWQgYWJvdXQgdGhlIGZpcnN0bmFtZSBhbmQgZmFtaWx5bmFtZSwgd2UgZGVjaWRlZCB0byBzZXBlcmF0ZSAgdGhlIHZhcmlhYmxlIG9uIGJhc2lzIG9mIElELg0KVGhlIGJlbG93IGNvZGUgc2hvd3MgaG93IHBsYXllcl9JRCBpcyBzZXBlcmF0ZWQgaW50byBOYW1lIGFuZCBJRCBieSB1c2luZyBzZXBlcmF0ZSgpIG9mIHRpZHlyIHBhY2thZ2UuDQoNCmBgYHtyfQ0KDQoNCmZvb3RiYWxsXzEgPC1mb290YmFsbCAlPiUgc2VwYXJhdGUgKGBQbGF5ZXIgSWRgLGludG89IGMoIk5hbWVzIiwiSUQiKSxzZXA9Jy8nKQ0KDQpoZWFkKGZvb3RiYWxsXzEpDQoNCmBgYA0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIA0KDQpNdXRhdGUoKSBmdW5jdGlvbiBpcyB1c2VkIGZvciBjcmVhdGlvbiBvZiBhIG5ldyBjb2x1bW4gY2FsbGVkIGNhcmVlcl95ZWFycy4NCkJ5IGxvb2tpbmcgYXQgdGhlIGRhdGEgd2Ugc2VlIHRoYXQgc29tZSBwbGF5ZXJzIGhhdmUgc3RhcnRlZCBwbGF5aW5nIGFuZCBnaXZlbiB1cCBpbiBzYW1lIHllYXIuQnV0IHdoZW4gd2UgdGFrZSB0aGUgZGlmZmVyZW5jZSBvZiB0aGVpciBzdGFydF95ZWFyICYgZW5kX3llYXIgd2UgZ2V0IDAgd2hpY2ggaXMgbG9naWNhbGx5IGluY29ycmVjdC5UbyBzb2x2ZSB0aGUgaXNzdWUgd2UgaGF2ZSBhZGRlZCAxIGV4cGxpY2l0eSAuDQoNCmBgYHtyfQ0KbXV0YXRlKGZvb3RiYWxsXzEsY2FyZWVyX3llYXJzPWVuZF95ZWFyLXN0YXJ0X3llYXIrMSkNCg0KYGBgDQoNCg0KIyMJU2NhbiBJIA0KDQpUaGUgbnVtZXJpYyB2YXJpYWJsZXMgYXJlIGZ1cnRoZXIgc3Vic2V0dGVkIGludG8gZm9vdGJhbGxfc3Vic2V0IHdpdGggdmFyaWFibGVzIElELCBIZWlnaHQgJiBXZWlnaHQgdG8gc2NhbiBhbnkgbWlzc2luZyB2YWx1ZXMvaW5jb25zaXN0ZW5jZXMuIA0KYGBge3J9DQoNCmZvb3RiYWxsX3N1YnNldDwtIGZvb3RiYWxsXzFbLGMoMiw3LDgpXQ0KDQpmb290YmFsbF9zdWJzZXQNCg0KYGBgDQoNClRvIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGluIGVhY2ggY29sdW1uICwgd2UgdXNlZCBjb2xzdW0gZnVuY3Rpb24uDQpXZSBmb3VuZCAxIG1pc3NpbmcgdmFsdWUgaW4gSGVpZ2h0IGNvbHVtbi4NCndoaWNoKCkgZnVuY3Rpb24gaXMgdGhlbiBhcHBsaWVkIHRvIGZpbmQgdGhlIGxvY2F0aW9uIG9mIE5BIGluIHRoYXQgdmFyaWFibGUNCg0KYGBge3J9DQoNCmNvbFN1bXMoaXMubmEoZm9vdGJhbGxfc3Vic2V0KSkNCg0Kd2hpY2goaXMubmEoZm9vdGJhbGxfc3Vic2V0JGBIZWlnaHQgKGluY2hlcylgKSkNCg0KYGBgDQpBcyB0aGVpciBhcmUgbWFueSB3YXlzIHRvIGhhbmRsZSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gZGF0YXNldCBzdWNoIGFzIHJlcGxhY2luZyBtaXNzaW5nIHZhbHVlcyB3aXRoIGNvbnN0YW50cyBvciBhIG1lYW4gb3IgbWVkaWFuIHZhbHVlcyBvZiB0aGUgdGhhdCB2YXJpYWJsZSAsaGVyZSB3ZSBoYXZlIGltcHV0ZWQgdGhlIG1pc3NpbmcgdmFsdWUgd2l0aCAnTWVkaWFuIFZhbHVlJ29mIGhlaWdodCBjb2x1bW4uQWZ0ZXIgaW1wdXRpbmcgdGhlIHZhbHVlcywgdGhlIHZhcmlhYmxlIGlzIGNoZWNrZWQgYWdhaW4gdG8gc2VlIGlmIGNoYW5nZXMgYXJlIGFwcGxpZWQuDQoNCmBgYHtyfQ0KDQpmb290YmFsbF9zdWJzZXQkYEhlaWdodCAoaW5jaGVzKWBbaXMubmEoZm9vdGJhbGxfc3Vic2V0JGBIZWlnaHQgKGluY2hlcylgKV0gPC1tZWRpYW4oZm9vdGJhbGxfc3Vic2V0JGBIZWlnaHQgKGluY2hlcylgLG5hLnJtID0gVFJVRSkNCg0KY29sU3Vtcyhpcy5uYShmb290YmFsbF9zdWJzZXQpKQ0KDQpgYGANCiMjCVNjYW4gSUkNCg0KV2Uga25vdyBtZXRob2RzIGZvciBkZXRlY3Rpbmcgb3V0bGllcnMgYXJlIGRpc3RhbmNlIGJhc2VkIG1ldGhvZCBhbmQgVHVya2V5J3MgbWV0aG9kIG9mIG91dGxpZXIgZGV0ZWN0aW9uLldlIHRoZXJlZm9yZSBjcmVhdGVkIGJveCBwbG90IHVzaW5nIFR1cmtleSdzIG1ldGhvZCBhbmQgb3V0bGllcnMgYXJlIHNlZW4uDQoNCmBgYHtyfQ0KZm9vdGJhbGxfc3Vic2V0JGBIZWlnaHQgKGluY2hlcylgICU+JSBib3hwbG90KG1haW49IkJveHBsb3QgZm9yIEhlaWdodCIseWxhYj0iSGVpZ2h0IEluIEluY2hlcyIpDQoNCmBgYA0KDQoNCkEgYm94IHBsb3QgaXMgc3RhbmRhcmRpc2VkIHdheSBvZiBkaXNwbGF5aW5nIGRpc3RyaWJ1dGlvbiBvZiBkYXRhIGJhc2VkIG9uIHN1bW1hcnkgc3RhdGlzdGljcyBhbmQgc2hvd3MgdGhlIG91dGxpZXJzIGFuZCB3aGF0IHRoZWlyIHZhbHVlcyBhcmUuDQpUaGUgZGlzdGFuY2UgYmFzZWQgKFotc2NvcmUpIGhlbHBzIGluIGNvbmZpcm1pbmcgdGhlIG51bWJlciBvZiBvdXRsaWVycyBkZXRlY3RlZC4NCldlIHRoZW4gY2FsY3VsYXRlZCB0aGUgei1zY29yZSBmb3IgaGVpZ2h0IHZhcmlhYmxlIC5BbHNvIHdlIGtub3cgdGhhdCBhbiBvYnNlcnZhdGlvbiBpcyBzYWlkIHRvIGJlIGFuIG91dGxpZXIgaWYgdGhlIGFic29sdXRlIHZhbHVlIG9mIGl0cyB6LXNjb3JlIGlzID4gMy5IZW5jZSwgYWNjb3JkaW5nIHRvIHotc2NvcmUgdGhlIHZhbHVlIGlzIDEgd2hpY2ggaXMgY29tcHV0ZWQgdXNpbmcgbGVuZ3RoIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCg0KenNjb3Jlc19mb290YmFsbF9oZWlnaHQgPC1mb290YmFsbF9zdWJzZXQkYEhlaWdodCAoaW5jaGVzKWAgJT4lIHNjb3Jlcyh0eXBlPSJ6IikNCg0KenNjb3Jlc19mb290YmFsbF9oZWlnaHQgJT4lIHN1bW1hcnkoKQ0KDQpsZW5ndGggKHdoaWNoKCBhYnMoenNjb3Jlc19mb290YmFsbF9oZWlnaHQpID4zICkpDQoNCg0KYGBgDQpUaGVpciBhcmUgbXVsdGlwbGUgd2F5cyB0byB0YWtlIGNhcmUgb2Ygb3V0bGllcnMgbGlrZSBleGNsdWRpbmcgLGltcHV0aW5nICwgY2FwcGluZyAmIGJpbm5pbmcuIEJ1dCBiZWZvcmUgd2UgdXNlIGFueSBvZiB0aGUgbWV0aG9kcyBtZW50aW9uZWQgYWJvdmUgLCB3ZSBuZWVkIHRvIGFuYWx5c2VkIGl0IHByb3Blcmx5IC5EdWUgdG8gaW5zdWZmaWNpZW50IHJlYXNvbnMgdG8gbWVhc3VyZSB0aGUgY2VydGFuaW50eSBvZiBvdXRsaWVycyB3ZSB0aGVuIHVzZWQgdXNlci1kZWZpbmVkIGNhcHBpbmcgbWV0aG9kLiAgDQpgYGB7cn0NCg0KY2FwIDwtIGZ1bmN0aW9uKHgpew0KICBxdWFudGlsZXMgPC0gcXVhbnRpbGUoIHgsIGMoLjA1LCAwLjI1LCAwLjc1LCAuOTUgKSApDQogIHhbIHggPCBxdWFudGlsZXNbMl0gLSAxLjUqSVFSKHgpIF0gPC0gcXVhbnRpbGVzWzFdDQogIHhbIHggPiBxdWFudGlsZXNbM10gKyAxLjUqSVFSKHgpIF0gPC0gcXVhbnRpbGVzWzRdDQogIHgNCn0NCg0KZm9vdGJhbGxfaGVpZ2h0X2NhcHBlZCA8LSBmb290YmFsbF9zdWJzZXQkYEhlaWdodCAoaW5jaGVzKWAgJT4lIGNhcCgpDQoNCmBgYA0KDQpUbyBjcm9zcyBjaGVjayBpZiBhbnkgb3V0bGllcnMgYXJlIHByZXNlbnQgLCB3ZSBwbG90dGVkIGEgYm94cGxvdCBmb3IgZm9vdGJhbGxfaGVpZ2h0X2NhcHBlZC4gDQoNCmBgYHtyfQ0KDQpmb290YmFsbF9oZWlnaHRfY2FwcGVkICU+JSBib3hwbG90KG1haW49IkJveHBsb3QgZm9yIEhlaWdodCIseWxhYj0iSGVpZ2h0IEluIEluY2hlcyIpDQoNCg0KDQpgYGANCg0KDQoNCg0KIyMJVHJhbnNmb3JtIA0KDQpCZWZvcmUgdGhlIGRhdGEgaXMgdXNlZCB0byBwcmVwYXJlIGEgbW9kZWwgLCB3ZSBhcHBsaWVkIHNvbWUgdHJhbnNmb3JtYXRpb25zIG9uIHRoZSB0aWR5IGRhdGEuDQpEYXRhIFRyYW5zZm9ybWF0aW9uIGlzIHVzZWQgaW4gbWFueSBzaXR1YXRpb25zIGxpa2UgLQ0KMSkgVG8gYWx0ZXIgdGhlIHNjYWxlIG9mIHZhcmlhYmxlIA0KMikgVG8gdHJhbnNmb3JtIG5vbi1saW5lYXIgaW50byBsaW5lYXIgcmVsYXRpb25zaGlwIA0KMykgVG8gcmVkdWNlIHNrZXduZXNzIGZvciBtYWtpbmcgaXQgYSBub3JtYWwgZGlzdHJpYnV0aW9uDQoNClRoZSBkaXN0cmlidXRpb24gb2YgaGVpZ2h0IHZhcmlhYmxlIGlzIHRoZW4gdmlzdWFsaXNlZCB1c2luZyBhIGhpc3RvZ3JhbS4NClRoZSBoaXN0b2dyYW0gYXBwZWFycyB0byBiZSBub3JtYWxseSBkaXN0cmlidXRlZC5UaGVyZWZvcmUgd2UgZGlkIG5vdCBhcHBsaWVkIGFueSBmdXJ0aGVyIHRyYW5zZm9yYW10aW9uIC4NCg0KYGBge3J9DQpoaXN0KGZvb3RiYWxsX2hlaWdodF9jYXBwZWQpDQoNCmBgYA0KDQoNCjxicj4NCjxicj4NCg==