Required packages

library(readr)
library(dplyr)
library(tidyr)
library(outliers)
library(forecast)

Executive Summary

This report aims to preprocess the players’ data in the videogame FIFA19. The datasets are download from Kaggle[https://www.kaggle.com/karangadiya/fifa19]. The first dataset is about the players’ information and the second one is about the players’ abilities in details. Each dataset has a variable of unique identify numbers.

In this report, the data of overall rating above 80 is filtered out and used. The data conversion of incorrect data types is executed after the two datasets merged together. Then, the dataset has been checked if it follows the principles of tidy data. A variable named “avg_ab” is created from the players’ detailed abilities to show the average abilities of them. Besides, four subsets of the data has been created according to the position of players, and a variable of average rating of each players’ required abilities is created as well in each subset.

Missing values and outliers are detected and handled by appropriate methods. Finally, different types of data transformation to the selected variable “avg_ab” are applied to compare, and conclusion is made that the Box-Cox transformation is the most useful method.

Data

These two datasets are collected from https://www.kaggle.com/karangadiya/fifa19.

fifa_a <- read_csv("fifa19a.csv")
Parsed with column specification:
cols(
  ID = col_double(),
  Name = col_character(),
  Age = col_double(),
  Nationality = col_character(),
  Overall = col_double(),
  Potential = col_double(),
  Club = col_character(),
  Value = col_double(),
  Wage = col_double(),
  Special = col_double(),
  `Preferred Foot` = col_character(),
  `International Reputation` = col_double(),
  `Weak Foot` = col_double(),
  `Skill Moves` = col_double(),
  Position = col_character(),
  `Jersey Number` = col_double(),
  Weight = col_character()
)
fifa_b <- read_csv("fifa19b.csv")
Parsed with column specification:
cols(
  .default = col_double()
)
See spec(...) for full column specifications.
fifa <- fifa_a %>% left_join(fifa_b, by = "ID")
fifa_fil <- fifa %>% filter(fifa$Overall >= 80)
head(fifa_fil)

Understand

str(fifa_fil)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    555 obs. of  51 variables:
 $ ID                      : num  158023 20801 190871 193080 192985 ...
 $ Name                    : chr  "L. Messi" "Cristiano Ronaldo" "Neymar Jr" "De Gea" ...
 $ Age                     : num  31 33 26 27 27 27 32 31 32 25 ...
 $ Nationality             : chr  "Argentina" "Portugal" "Brazil" "Spain" ...
 $ Overall                 : num  94 94 92 91 91 91 91 91 91 90 ...
 $ Potential               : num  94 94 93 93 92 91 91 91 91 93 ...
 $ Club                    : chr  "FC Barcelona" "Juventus" "Paris Saint-Germain" "Manchester United" ...
 $ Value                   : num  1.10e+08 7.70e+07 1.18e+08 7.20e+07 1.02e+08 ...
 $ Wage                    : num  565000 405000 290000 260000 355000 340000 420000 455000 380000 94000 ...
 $ Special                 : num  2202 2228 2143 1471 2281 ...
 $ Preferred Foot          : chr  "Left" "Right" "Right" "Right" ...
 $ International Reputation: num  5 5 5 4 4 4 4 5 4 3 ...
 $ Weak Foot               : num  4 4 5 3 5 4 4 4 3 3 ...
 $ Skill Moves             : num  4 5 5 1 4 4 4 3 3 1 ...
 $ Position                : chr  "RF" "ST" "LW" "GK" ...
 $ Jersey Number           : num  10 7 10 1 7 10 10 9 15 1 ...
 $ Weight                  : chr  "159lbs" "183lbs" "150lbs" "168lbs" ...
 $ Crossing                : num  84 84 79 17 93 81 86 77 66 13 ...
 $ Finishing               : num  95 94 87 13 82 84 72 93 60 11 ...
 $ HeadingAccuracy         : num  70 89 62 21 55 61 55 77 91 15 ...
 $ ShortPassing            : num  90 81 84 50 92 89 93 82 78 29 ...
 $ Volleys                 : num  86 87 84 13 82 80 76 88 66 13 ...
 $ Dribbling               : num  97 88 96 18 86 95 90 87 63 12 ...
 $ Curve                   : num  93 81 88 21 85 83 85 86 74 13 ...
 $ FKAccuracy              : num  94 76 87 19 83 79 78 84 72 14 ...
 $ LongPassing             : num  87 77 78 51 91 83 88 64 77 26 ...
 $ BallControl             : num  96 94 95 42 91 94 93 90 84 16 ...
 $ Acceleration            : num  91 89 94 57 78 94 80 86 76 43 ...
 $ SprintSpeed             : num  86 91 90 58 76 88 72 75 75 60 ...
 $ Agility                 : num  91 87 96 60 79 95 93 82 78 67 ...
 $ Reactions               : num  95 96 94 90 91 90 90 92 85 86 ...
 $ Balance                 : num  95 70 84 43 77 94 94 83 66 49 ...
 $ ShotPower               : num  85 95 80 31 91 82 79 86 79 22 ...
 $ Jumping                 : num  68 95 61 67 63 56 68 69 93 76 ...
 $ Stamina                 : num  72 88 81 43 90 83 89 90 84 41 ...
 $ Strength                : num  59 79 49 64 75 66 58 83 83 78 ...
 $ LongShots               : num  94 93 82 12 91 80 82 85 59 12 ...
 $ Aggression              : num  48 63 56 38 76 54 62 87 88 34 ...
 $ Interceptions           : num  22 29 36 30 61 41 83 41 90 19 ...
 $ Positioning             : num  94 95 89 12 87 87 79 92 60 11 ...
 $ Vision                  : num  94 82 87 68 94 89 92 84 63 70 ...
 $ Penalties               : num  75 85 81 40 79 86 82 85 75 11 ...
 $ Composure               : num  96 95 94 68 88 91 84 85 82 70 ...
 $ Marking                 : num  33 28 27 15 68 34 60 62 87 27 ...
 $ StandingTackle          : num  28 31 24 21 58 27 76 45 92 12 ...
 $ SlidingTackle           : num  26 23 33 13 51 22 73 38 91 18 ...
 $ GKDiving                : num  6 7 9 90 15 11 13 27 11 86 ...
 $ GKHandling              : num  11 11 9 85 13 12 9 25 8 92 ...
 $ GKKicking               : num  15 15 15 87 5 6 7 31 9 78 ...
 $ GKPositioning           : num  14 14 15 88 10 8 14 33 7 88 ...
 $ GKReflexes              : num  8 11 11 94 13 8 9 37 11 89 ...
fifa_fil$ID <- fifa_fil$ID %>% as.character()
class(fifa_fil$ID)
[1] "character"
fifa_fil$`Preferred Foot` <- fifa_fil$`Preferred Foot` %>% as.factor()
fifa_fil$`Preferred Foot` <- fifa_fil$`Preferred Foot` %>% factor(levels = c("Left", "Right"))
levels(fifa_fil$`Preferred Foot`)
[1] "Left"  "Right"
fifa_fil <- fifa_fil %>% separate(Weight, into = c("weight", "lbs"), sep = 3)
fifa_fil$weight <- fifa_fil$weight %>% as.numeric()
class(fifa_fil$weight)
[1] "numeric"
fifa_fil <- fifa_fil[, -18]
head(fifa_fil)
fifa_fil$Position <- fifa_fil$Position %>% as.factor()
fifa_fil$Position <- fifa_fil$Position %>% factor(levels = c("ST","LS","RS","CF","LF","RF","LW","RW","CAM","LAM","RAM","CM","LCM","RCM","LM","RM","CDM","LDM","RDM","CB","LCB","RCB","LWB","RWB","LB","RB","GK"))
levels(fifa_fil$Position)
 [1] "ST"  "LS"  "RS"  "CF"  "LF"  "RF"  "LW"  "RW"  "CAM" "LAM" "RAM" "CM"  "LCM" "RCM" "LM" 
[16] "RM"  "CDM" "LDM" "RDM" "CB"  "LCB" "RCB" "LWB" "RWB" "LB"  "RB"  "GK" 

Tidy & Manipulate Data I

This data is a tidy data as it matches the principles of tidy data:

Tidy & Manipulate Data II

# Use mutate function
fifa_fil <-  mutate(fifa_fil, avg_ab = (Crossing + Finishing + HeadingAccuracy + ShortPassing + Volleys + Dribbling + Curve + FKAccuracy + LongPassing + BallControl + Acceleration + SprintSpeed + Agility + Reactions + Balance + ShotPower + Jumping + Stamina + Strength + LongShots + Aggression + Interceptions + Positioning + Vision + Penalties + Composure + Marking + StandingTackle + SlidingTackle + GKDiving + GKHandling + GKKicking + GKPositioning + GKReflexes) / 34)
head(fifa_fil$avg_ab)
[1] 67.58824 68.32353 65.79412 45.26471 69.67647 65.67647
# For attackers
fifa_attack <- fifa_fil %>% filter(Position == "ST" | Position == "LS" | Position == "RS" | Position == "CF" | Position == "LF" | Position == "RF" | Position == "LW" | Position == "RW")
fifa_attack <- mutate(fifa_attack, avg_attab = (Crossing + Finishing + HeadingAccuracy + ShortPassing + Volleys + Dribbling + Curve + FKAccuracy+ BallControl + Acceleration + SprintSpeed + Agility + Reactions + Balance + ShotPower + Jumping + Stamina + Strength + LongShots + Positioning + Vision + Penalties + Composure) / 23)
head(fifa_attack)
# For Midfielders
fifa_midf <- fifa_fil %>%  filter(Position == "CAM" | Position == "LAM" | Position == "RAM" | Position == "CM"  | Position == "LCM" | Position == "RCM" | Position == "LM"  | Position == "RM"  | Position == "CDM" | Position == "LDM" | Position =="RDM")
fifa_midf <- mutate(fifa_midf, avg_midab = (Crossing + HeadingAccuracy + ShortPassing + Volleys + Dribbling + Curve + LongPassing + BallControl + Acceleration + SprintSpeed + Agility + Reactions + Balance + ShotPower + Jumping + Stamina + Strength + LongShots + Aggression + Interceptions + Positioning + Vision + StandingTackle) / 23)
head(fifa_midf)
# For Defenders
fifa_def <- fifa_fil %>% filter(Position == "CB" | Position == "LCB" | Position == "RCB" | Position == "LWB" | Position == "RWB" | Position == "LB" | Position == "RB")
fifa_def <- mutate(fifa_def, avg_defab = (HeadingAccuracy + ShortPassing + LongPassing + Acceleration + SprintSpeed + Reactions + Balance + Jumping + Stamina + Strength + Aggression + Interceptions + Positioning + Composure + Marking + StandingTackle + SlidingTackle) / 17)
head(fifa_def)
# For GoalKeepers
fifa_gk <- fifa_fil %>% filter(Position == "GK")
fifa_gk <- mutate(fifa_gk, avg_gk = (Acceleration + Reactions + Jumping + Vision + Composure + GKDiving + GKHandling + GKKicking + GKPositioning + GKReflexes) / 10)
head(fifa_gk)

Scan I

colSums(is.na(fifa_fil))
                      ID                     Name                      Age 
                       0                        0                        0 
             Nationality                  Overall                Potential 
                       0                        0                        0 
                    Club                    Value                     Wage 
                       2                        2                        2 
                 Special           Preferred Foot International Reputation 
                       0                        0                        0 
               Weak Foot              Skill Moves                 Position 
                       0                        0                        0 
           Jersey Number                   weight                 Crossing 
                       0                        0                        0 
               Finishing          HeadingAccuracy             ShortPassing 
                       0                        0                        0 
                 Volleys                Dribbling                    Curve 
                       0                        0                        0 
              FKAccuracy              LongPassing              BallControl 
                       0                        0                        0 
            Acceleration              SprintSpeed                  Agility 
                       0                        0                        0 
               Reactions                  Balance                ShotPower 
                       0                        0                        0 
                 Jumping                  Stamina                 Strength 
                       0                        0                        0 
               LongShots               Aggression            Interceptions 
                       0                        0                        0 
             Positioning                   Vision                Penalties 
                       0                        0                        0 
               Composure                  Marking           StandingTackle 
                       0                        0                        0 
           SlidingTackle                 GKDiving               GKHandling 
                       0                        0                        0 
               GKKicking            GKPositioning               GKReflexes 
                       0                        0                        0 
                  avg_ab 
                       0 
fifa_fil <- fifa_fil %>% na.omit(fifa_fil$Club)
# Check the missing value again
colSums(is.na(fifa_fil))
                      ID                     Name                      Age 
                       0                        0                        0 
             Nationality                  Overall                Potential 
                       0                        0                        0 
                    Club                    Value                     Wage 
                       0                        0                        0 
                 Special           Preferred Foot International Reputation 
                       0                        0                        0 
               Weak Foot              Skill Moves                 Position 
                       0                        0                        0 
           Jersey Number                   weight                 Crossing 
                       0                        0                        0 
               Finishing          HeadingAccuracy             ShortPassing 
                       0                        0                        0 
                 Volleys                Dribbling                    Curve 
                       0                        0                        0 
              FKAccuracy              LongPassing              BallControl 
                       0                        0                        0 
            Acceleration              SprintSpeed                  Agility 
                       0                        0                        0 
               Reactions                  Balance                ShotPower 
                       0                        0                        0 
                 Jumping                  Stamina                 Strength 
                       0                        0                        0 
               LongShots               Aggression            Interceptions 
                       0                        0                        0 
             Positioning                   Vision                Penalties 
                       0                        0                        0 
               Composure                  Marking           StandingTackle 
                       0                        0                        0 
           SlidingTackle                 GKDiving               GKHandling 
                       0                        0                        0 
               GKKicking            GKPositioning               GKReflexes 
                       0                        0                        0 
                  avg_ab 
                       0 

From the result, we can see that all the missing values are located in same rows, thus, all the missing values are deleted. However, if the missing values are not located in same rows, the mean imputation to the numerical missing values will be used.

Scan II

# For "Overall"
z.score_all <- fifa_fil$Overall %>% scores(type = "z")
z.score_all %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.9726 -0.9726 -0.2307  0.0000  0.5111  4.2204 
# Visualization
fifa_fil$Overall %>% boxplot(main = "Overall", ylab = "Rating")

# Find total number of outliers
length(which( abs(z.score_all) > 3))
[1] 9
# Delete the outliers
clean_overall <- fifa_fil$Overall[ - which( abs(z.score_all) > 3)]
head(clean_overall)
[1] 90 90 90 90 90 89
# For "Poetential"
z.score_pot <- fifa_fil$Potential %>% scores(type = "z")
z.score_pot %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-1.2943 -0.7011 -0.1078  0.0000  0.7820  3.1550 
# Visualization
fifa_fil$Potential %>% boxplot(main = "Potential", ylab = "Rating")

# Find the total numbers of outliers
length(which( abs(z.score_pot)> 3))
[1] 1
# Delete the outlier
clean_pot <- fifa_fil$Potential[ - which( abs(z.score_pot) > 3)]
head(clean_pot)
[1] 94 94 93 93 92 91
# For "weight"
z.score_wei <- fifa_fil$weight %>% scores(type = "z")
z.score_wei %>% summary()
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-2.36436 -0.66693 -0.02307  0.00000  0.73784  2.90354 
# Find the totoal number of outliers
length(which( abs(z.score_wei)> 3))
[1] 0
# For avg_ab
z.score_avgab <- fifa_fil$avg_ab %>% scores(type = "z")
z.score_avgab %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.2000 -0.1669  0.3157  0.0000  0.6116  1.4923 
# Find the totoal number of outliers
length(which( abs(z.score_avgab) > 3))
[1] 1

Capping, one of the approaches to handling outliers will be used

# Define a function of capping
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
}
avg_ab_capped <- fifa_fil$avg_ab %>% cap()
head(avg_ab_capped)
[1] 67.58824 68.32353 65.79412 38.08235 69.67647 65.67647

Transform

In this section, the variable will be used to transformed is “Overall”

# Gain the histogram of the variable to determine the method of transformation
hist(fifa_fil$Overall)

This is a right-skewed distribution, many transformation will be used to compare.

boxcox_overall <- BoxCox(fifa_fil$Overall, lambda = "auto")
# Check the result
hist(boxcox_overall)

log_oveall <- log10(fifa_fil$Overall)
# Check the result
hist(log_oveall)

ln_overall <- log(fifa_fil$Overall)
# Check the result
hist(ln_overall)

recip_overall <- 1/fifa_fil$Overall
# Check the result
hist(recip_overall)

From the results, it can be seen that the box-cox transformation works best on this data.

LS0tDQp0aXRsZTogIk1BVEgyMzQ5IERhdGEgUHJlcHJvY2Vzc2luZyINCmF1dGhvcjogIlNpY2hlbiBIdSBTMzc1NDY1NyINCnN1YnRpdGxlOiBBc3NpZ25tZW50IDMNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkob3V0bGllcnMpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KYGBgDQoNCg0KIyMgRXhlY3V0aXZlIFN1bW1hcnkgDQoNClRoaXMgcmVwb3J0IGFpbXMgdG8gcHJlcHJvY2VzcyB0aGUgcGxheWVycycgZGF0YSBpbiB0aGUgdmlkZW9nYW1lIEZJRkExOS4gVGhlIGRhdGFzZXRzIGFyZSBkb3dubG9hZCBmcm9tIEthZ2dsZVtodHRwczovL3d3dy5rYWdnbGUuY29tL2thcmFuZ2FkaXlhL2ZpZmExOV0uIFRoZSBmaXJzdCBkYXRhc2V0IGlzIGFib3V0IHRoZSBwbGF5ZXJzJyBpbmZvcm1hdGlvbiBhbmQgdGhlIHNlY29uZCBvbmUgaXMgYWJvdXQgdGhlIHBsYXllcnMnIGFiaWxpdGllcyBpbiBkZXRhaWxzLiBFYWNoIGRhdGFzZXQgaGFzIGEgdmFyaWFibGUgb2YgdW5pcXVlIGlkZW50aWZ5IG51bWJlcnMuDQoNCkluIHRoaXMgcmVwb3J0LCB0aGUgZGF0YSBvZiBvdmVyYWxsIHJhdGluZyBhYm92ZSA4MCBpcyBmaWx0ZXJlZCBvdXQgYW5kIHVzZWQuIFRoZSBkYXRhIGNvbnZlcnNpb24gb2YgaW5jb3JyZWN0IGRhdGEgdHlwZXMgaXMgZXhlY3V0ZWQgYWZ0ZXIgdGhlIHR3byBkYXRhc2V0cyBtZXJnZWQgdG9nZXRoZXIuIFRoZW4sIHRoZSBkYXRhc2V0IGhhcyBiZWVuIGNoZWNrZWQgaWYgaXQgZm9sbG93cyB0aGUgcHJpbmNpcGxlcyBvZiB0aWR5IGRhdGEuIEEgdmFyaWFibGUgbmFtZWQgImF2Z19hYiIgaXMgY3JlYXRlZCBmcm9tIHRoZSBwbGF5ZXJzJyBkZXRhaWxlZCBhYmlsaXRpZXMgdG8gc2hvdyB0aGUgYXZlcmFnZSBhYmlsaXRpZXMgb2YgdGhlbS4gQmVzaWRlcywgZm91ciBzdWJzZXRzIG9mIHRoZSBkYXRhIGhhcyBiZWVuIGNyZWF0ZWQgYWNjb3JkaW5nIHRvIHRoZSBwb3NpdGlvbiBvZiBwbGF5ZXJzLCBhbmQgYSB2YXJpYWJsZSBvZiBhdmVyYWdlIHJhdGluZyBvZiBlYWNoIHBsYXllcnMnIHJlcXVpcmVkIGFiaWxpdGllcyBpcyBjcmVhdGVkIGFzIHdlbGwgaW4gZWFjaCBzdWJzZXQuDQoNCk1pc3NpbmcgdmFsdWVzIGFuZCBvdXRsaWVycyBhcmUgZGV0ZWN0ZWQgYW5kIGhhbmRsZWQgYnkgYXBwcm9wcmlhdGUgbWV0aG9kcy4gRmluYWxseSwgZGlmZmVyZW50IHR5cGVzIG9mIGRhdGEgdHJhbnNmb3JtYXRpb24gdG8gdGhlIHNlbGVjdGVkIHZhcmlhYmxlICJhdmdfYWIiIGFyZSBhcHBsaWVkIHRvIGNvbXBhcmUsIGFuZCBjb25jbHVzaW9uIGlzIG1hZGUgdGhhdCB0aGUgQm94LUNveCB0cmFuc2Zvcm1hdGlvbiBpcyB0aGUgbW9zdCB1c2VmdWwgbWV0aG9kLg0KDQojIyBEYXRhDQpUaGVzZSB0d28gZGF0YXNldHMgYXJlIGNvbGxlY3RlZCBmcm9tIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20va2FyYW5nYWRpeWEvZmlmYTE5LiANCg0KKiBUaGUgdmFyaWFibGVzIG9mICJmaWZhX2EiIGluY2x1ZGVzOg0KICAgICsgSUQ6IHVuaXF1ZSBpZCBmb3IgZXZlcnkgcGxheWVyDQogICAgKyBOYW1lDQogICAgKyBBZ2UNCiAgICArIE5hdGlvbmFsaXR5DQogICAgKyBPdmVyYWxsOiBvdmVyYWxsIHJhdGluZyBvZiBlYWNoIHBsYXllcg0KICAgICsgUG90ZW50aWFsOiBwb3RlbnRpYWwgcmF0aW5nIG9mIGVhY2ggcGxheWVyDQogICAgKyBDbHViDQogICAgKyBWYWx1ZTogY3VycmVudCBtYXJrZXQgdmFsdWUgaW4gRXVyb3MNCiAgICArIFdhZ2U6IGluIEV1cm9zDQogICAgKyBTcGVjaWFsOiBzcGVjaWFsaXR5DQogICAgKyBQcmVmZXJyZWQgRm9vdDogcmF0aW5nIG9uIHNjYWxlIG9mIDUNCiAgICArIEludGVybmF0aW9uYWwgUmVwdXRhdGlvbjogcmF0aW5nIG9uIHNjYWxlIG9mIDUNCiAgICArIFdlYWsgRm9vdDogYWJpbGl0eSByYXRpbmcgb2Ygd2VhayBmb290IG9uIHNjYWxlIG9mIDUNCiAgICArIFNraWxsIE1vdmVzOiBhYmlsaXR5IG9mIHNraWxsIG1vdmVzIG9uIHNjYWxlIG9mIDUNCiAgICArIFBvc2l0aW9uOiBwbGF5aW5nIHBvc2l0aW9uIG9uIGZpZWxkDQogICAgKyBKZXJzZXkgTnVtYmVyDQogICAgKyB3ZWlnaHQNCg0KKiBUaGUgdmFyaWFibGVzIG9mICJmaWZhX2IiIGluY2x1ZGVzOg0KICAgICsgSUQ6IHVuaXF1ZSBpZCBmb3IgZXZlcnkgcGxheWVyDQogICAgKyBFYWNoIHBsYXllcidzIGFiaWxpdHkgcmF0aW5nIG9uIHNjYWxlIG9mIDEwMCwgd2hpY2ggY2FuIGJlIGV4cGxhaW5lZCBieSBlYWNoIHZhcmlhYmxlIG5hbWUuDQoNCiogSW1wb3J0IHRoZSBkYXRhc2V0cw0KYGBge3J9DQpmaWZhX2EgPC0gcmVhZF9jc3YoImZpZmExOWEuY3N2IikNCmZpZmFfYiA8LSByZWFkX2NzdigiZmlmYTE5Yi5jc3YiKQ0KYGBgDQoNCiogTWVyZ2UgdGhlIHR3byBkYXRhc2V0cw0KYGBge3IgZWNobz1UUlVFfQ0KZmlmYSA8LSBmaWZhX2EgJT4lIGxlZnRfam9pbihmaWZhX2IsIGJ5ID0gIklEIikNCmBgYA0KDQoqIEFzIHRoZSBkYXRhc2V0IGlzIHRvbyBsYXJnZSwgbWFrZSBhIHN1YnNldCBvZiBwbGF5ZXJzIG92ZXJhbGwgcmF0aW5nIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA4MC4NCmBgYHtyfQ0KZmlmYV9maWwgPC0gZmlmYSAlPiUgZmlsdGVyKGZpZmEkT3ZlcmFsbCA+PSA4MCkNCmhlYWQoZmlmYV9maWwpDQpgYGANCiMjIFVuZGVyc3RhbmQgDQoNCiogU3VtbWFyaXNlIHRoZSB0eXBlcyBvZiB2YXJpYWJsZXMgYW5kIGRhdGEgc3RydWN0dXJlcw0KYGBge3J9DQpzdHIoZmlmYV9maWwpDQpgYGANCg0KKiBDb252ZXJ0ICJJRCIgdG8gY2hhcmFjdGVyIGFzIGl0IGlzIHVuaXF1ZQ0KYGBge3J9DQpmaWZhX2ZpbCRJRCA8LSBmaWZhX2ZpbCRJRCAlPiUgYXMuY2hhcmFjdGVyKCkNCmNsYXNzKGZpZmFfZmlsJElEKQ0KYGBgDQoqIENvbnZlcnQgIlByZWZlcnJlZCBGb290IiB0byBmYWN0b3INCmBgYHtyfQ0KZmlmYV9maWwkYFByZWZlcnJlZCBGb290YCA8LSBmaWZhX2ZpbCRgUHJlZmVycmVkIEZvb3RgICU+JSBhcy5mYWN0b3IoKQ0KZmlmYV9maWwkYFByZWZlcnJlZCBGb290YCA8LSBmaWZhX2ZpbCRgUHJlZmVycmVkIEZvb3RgICU+JSBmYWN0b3IobGV2ZWxzID0gYygiTGVmdCIsICJSaWdodCIpKQ0KbGV2ZWxzKGZpZmFfZmlsJGBQcmVmZXJyZWQgRm9vdGApDQpgYGANCiogU2VwYXJhdGUgImxicyIgZnJvbSB2YXJpYWJsZSAiV2VpZ2h0IiwgYW5kIG1ha2UgdGhlIHZhcmlhYmxlIGFzIG51bWVyaWMNCmBgYHtyfQ0KZmlmYV9maWwgPC0gZmlmYV9maWwgJT4lIHNlcGFyYXRlKFdlaWdodCwgaW50byA9IGMoIndlaWdodCIsICJsYnMiKSwgc2VwID0gMykNCmZpZmFfZmlsJHdlaWdodCA8LSBmaWZhX2ZpbCR3ZWlnaHQgJT4lIGFzLm51bWVyaWMoKQ0KY2xhc3MoZmlmYV9maWwkd2VpZ2h0KQ0KYGBgDQoqIERlbGV0ZSB0aGUgY29sdW1uICJsYnMiDQpgYGB7cn0NCmZpZmFfZmlsIDwtIGZpZmFfZmlsWywgLTE4XQ0KaGVhZChmaWZhX2ZpbCkNCmBgYA0KDQoqIENvbnZlcnQgIlBvc2l0aW9uIiB0byBmYWN0b3INCmBgYHtyfQ0KZmlmYV9maWwkUG9zaXRpb24gPC0gZmlmYV9maWwkUG9zaXRpb24gJT4lIGFzLmZhY3RvcigpDQpmaWZhX2ZpbCRQb3NpdGlvbiA8LSBmaWZhX2ZpbCRQb3NpdGlvbiAlPiUgZmFjdG9yKGxldmVscyA9IGMoIlNUIiwiTFMiLCJSUyIsIkNGIiwiTEYiLCJSRiIsIkxXIiwiUlciLCJDQU0iLCJMQU0iLCJSQU0iLCJDTSIsIkxDTSIsIlJDTSIsIkxNIiwiUk0iLCJDRE0iLCJMRE0iLCJSRE0iLCJDQiIsIkxDQiIsIlJDQiIsIkxXQiIsIlJXQiIsIkxCIiwiUkIiLCJHSyIpKQ0KbGV2ZWxzKGZpZmFfZmlsJFBvc2l0aW9uKQ0KYGBgDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIEkgDQoNClRoaXMgZGF0YSBpcyBhIHRpZHkgZGF0YSBhcyBpdCBtYXRjaGVzIHRoZSBwcmluY2lwbGVzIG9mIHRpZHkgZGF0YToNCg0KKiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uLg0KKiBFYWNoIG9ic2VydmF0aW9uIGZvcm1zIGEgcm93Lg0KKiBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUuDQoNCiMjIFRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkNCg0KKiBDcmVhdGUgYSB2YXJpYWJsZSBvZiBlYWNoIHBsYXllcidzIGF2ZXJhZ2UgYWJpbGl0eSwgbmFtZSBpdCBhcyAiYXZnX2FiIg0KYGBge3IgZWNobz1UUlVFfQ0KIyBVc2UgbXV0YXRlIGZ1bmN0aW9uDQpmaWZhX2ZpbCA8LSAgbXV0YXRlKGZpZmFfZmlsLCBhdmdfYWIgPSAoQ3Jvc3NpbmcgKyBGaW5pc2hpbmcgKyBIZWFkaW5nQWNjdXJhY3kgKyBTaG9ydFBhc3NpbmcgKyBWb2xsZXlzICsgRHJpYmJsaW5nICsgQ3VydmUgKyBGS0FjY3VyYWN5ICsgTG9uZ1Bhc3NpbmcgKyBCYWxsQ29udHJvbCArIEFjY2VsZXJhdGlvbiArIFNwcmludFNwZWVkICsgQWdpbGl0eSArIFJlYWN0aW9ucyArIEJhbGFuY2UgKyBTaG90UG93ZXIgKyBKdW1waW5nICsgU3RhbWluYSArIFN0cmVuZ3RoICsgTG9uZ1Nob3RzICsgQWdncmVzc2lvbiArIEludGVyY2VwdGlvbnMgKyBQb3NpdGlvbmluZyArIFZpc2lvbiArIFBlbmFsdGllcyArIENvbXBvc3VyZSArIE1hcmtpbmcgKyBTdGFuZGluZ1RhY2tsZSArIFNsaWRpbmdUYWNrbGUgKyBHS0RpdmluZyArIEdLSGFuZGxpbmcgKyBHS0tpY2tpbmcgKyBHS1Bvc2l0aW9uaW5nICsgR0tSZWZsZXhlcykgLyAzNCkNCg0KaGVhZChmaWZhX2ZpbCRhdmdfYWIpDQpgYGANCg0KKiBDcmVhdGUgc3Vic2V0cyBvZiBlYWNoIHBvc2l0aW9uIGFuZCBjcmVhdGUgdmFyaWFibGVzIG9mIHRoZSBhdmVyYWdlIGFiaWxpdHkgb2YgZWFjaCBwb3NpdGlvbiBuZWVkDQpgYGB7cn0NCiMgRm9yIGF0dGFja2Vycw0KZmlmYV9hdHRhY2sgPC0gZmlmYV9maWwgJT4lIGZpbHRlcihQb3NpdGlvbiA9PSAiU1QiIHwgUG9zaXRpb24gPT0gIkxTIiB8IFBvc2l0aW9uID09ICJSUyIgfCBQb3NpdGlvbiA9PSAiQ0YiIHwgUG9zaXRpb24gPT0gIkxGIiB8IFBvc2l0aW9uID09ICJSRiIgfCBQb3NpdGlvbiA9PSAiTFciIHwgUG9zaXRpb24gPT0gIlJXIikNCg0KZmlmYV9hdHRhY2sgPC0gbXV0YXRlKGZpZmFfYXR0YWNrLCBhdmdfYXR0YWIgPSAoQ3Jvc3NpbmcgKyBGaW5pc2hpbmcgKyBIZWFkaW5nQWNjdXJhY3kgKyBTaG9ydFBhc3NpbmcgKyBWb2xsZXlzICsgRHJpYmJsaW5nICsgQ3VydmUgKyBGS0FjY3VyYWN5KyBCYWxsQ29udHJvbCArIEFjY2VsZXJhdGlvbiArIFNwcmludFNwZWVkICsgQWdpbGl0eSArIFJlYWN0aW9ucyArIEJhbGFuY2UgKyBTaG90UG93ZXIgKyBKdW1waW5nICsgU3RhbWluYSArIFN0cmVuZ3RoICsgTG9uZ1Nob3RzICsgUG9zaXRpb25pbmcgKyBWaXNpb24gKyBQZW5hbHRpZXMgKyBDb21wb3N1cmUpIC8gMjMpDQpoZWFkKGZpZmFfYXR0YWNrKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGb3IgTWlkZmllbGRlcnMNCmZpZmFfbWlkZiA8LSBmaWZhX2ZpbCAlPiUgIGZpbHRlcihQb3NpdGlvbiA9PSAiQ0FNIiB8IFBvc2l0aW9uID09ICJMQU0iIHwgUG9zaXRpb24gPT0gIlJBTSIgfCBQb3NpdGlvbiA9PSAiQ00iICB8IFBvc2l0aW9uID09ICJMQ00iIHwgUG9zaXRpb24gPT0gIlJDTSIgfCBQb3NpdGlvbiA9PSAiTE0iICB8IFBvc2l0aW9uID09ICJSTSIgIHwgUG9zaXRpb24gPT0gIkNETSIgfCBQb3NpdGlvbiA9PSAiTERNIiB8IFBvc2l0aW9uID09IlJETSIpDQoNCmZpZmFfbWlkZiA8LSBtdXRhdGUoZmlmYV9taWRmLCBhdmdfbWlkYWIgPSAoQ3Jvc3NpbmcgKyBIZWFkaW5nQWNjdXJhY3kgKyBTaG9ydFBhc3NpbmcgKyBWb2xsZXlzICsgRHJpYmJsaW5nICsgQ3VydmUgKyBMb25nUGFzc2luZyArIEJhbGxDb250cm9sICsgQWNjZWxlcmF0aW9uICsgU3ByaW50U3BlZWQgKyBBZ2lsaXR5ICsgUmVhY3Rpb25zICsgQmFsYW5jZSArIFNob3RQb3dlciArIEp1bXBpbmcgKyBTdGFtaW5hICsgU3RyZW5ndGggKyBMb25nU2hvdHMgKyBBZ2dyZXNzaW9uICsgSW50ZXJjZXB0aW9ucyArIFBvc2l0aW9uaW5nICsgVmlzaW9uICsgU3RhbmRpbmdUYWNrbGUpIC8gMjMpDQpoZWFkKGZpZmFfbWlkZikNCmBgYA0KDQpgYGB7cn0NCiMgRm9yIERlZmVuZGVycw0KZmlmYV9kZWYgPC0gZmlmYV9maWwgJT4lIGZpbHRlcihQb3NpdGlvbiA9PSAiQ0IiIHwgUG9zaXRpb24gPT0gIkxDQiIgfCBQb3NpdGlvbiA9PSAiUkNCIiB8IFBvc2l0aW9uID09ICJMV0IiIHwgUG9zaXRpb24gPT0gIlJXQiIgfCBQb3NpdGlvbiA9PSAiTEIiIHwgUG9zaXRpb24gPT0gIlJCIikNCg0KZmlmYV9kZWYgPC0gbXV0YXRlKGZpZmFfZGVmLCBhdmdfZGVmYWIgPSAoSGVhZGluZ0FjY3VyYWN5ICsgU2hvcnRQYXNzaW5nICsgTG9uZ1Bhc3NpbmcgKyBBY2NlbGVyYXRpb24gKyBTcHJpbnRTcGVlZCArIFJlYWN0aW9ucyArIEJhbGFuY2UgKyBKdW1waW5nICsgU3RhbWluYSArIFN0cmVuZ3RoICsgQWdncmVzc2lvbiArIEludGVyY2VwdGlvbnMgKyBQb3NpdGlvbmluZyArIENvbXBvc3VyZSArIE1hcmtpbmcgKyBTdGFuZGluZ1RhY2tsZSArIFNsaWRpbmdUYWNrbGUpIC8gMTcpDQpoZWFkKGZpZmFfZGVmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGb3IgR29hbEtlZXBlcnMNCmZpZmFfZ2sgPC0gZmlmYV9maWwgJT4lIGZpbHRlcihQb3NpdGlvbiA9PSAiR0siKQ0KDQpmaWZhX2drIDwtIG11dGF0ZShmaWZhX2drLCBhdmdfZ2sgPSAoQWNjZWxlcmF0aW9uICsgUmVhY3Rpb25zICsgSnVtcGluZyArIFZpc2lvbiArIENvbXBvc3VyZSArIEdLRGl2aW5nICsgR0tIYW5kbGluZyArIEdLS2lja2luZyArIEdLUG9zaXRpb25pbmcgKyBHS1JlZmxleGVzKSAvIDEwKQ0KaGVhZChmaWZhX2drKQ0KYGBgDQojIwlTY2FuIEkgDQoNCiogU2NhbiB0aGUgZGF0YSBmb3IgbWlzc2luZyB2YWx1ZXMNCg0KYGBge3J9DQpjb2xTdW1zKGlzLm5hKGZpZmFfZmlsKSkNCmBgYA0KDQoqIEZvciB0aGUgdmFyaWFibGUgIkNsdWIiLCBhcyBpdCBzaG91bGQgYmUgdGhlIHJlYWwgY2x1YiBvZiB0aGUgcGxheWVyLCBhbmQgaXQgaXMgbm90IGVhc3kgdG8gZmluZCBpdCB0byByZXBsYWNlIHRoZSBtaXNzaW5nIHZhbHVlcywgdGh1cywgZGVsZXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBhcyB0aGUgbnVtYmVyIG9mIHRoZW0gaXMgbm90IHNpZ25pZmljYW50Lg0KYGBge3J9DQpmaWZhX2ZpbCA8LSBmaWZhX2ZpbCAlPiUgbmEub21pdChmaWZhX2ZpbCRDbHViKQ0KIyBDaGVjayB0aGUgbWlzc2luZyB2YWx1ZSBhZ2Fpbg0KY29sU3Vtcyhpcy5uYShmaWZhX2ZpbCkpDQpgYGANCkZyb20gdGhlIHJlc3VsdCwgd2UgY2FuIHNlZSB0aGF0IGFsbCB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIGxvY2F0ZWQgaW4gc2FtZSByb3dzLCB0aHVzLCBhbGwgdGhlIG1pc3NpbmcgdmFsdWVzIGFyZSBkZWxldGVkLiBIb3dldmVyLCBpZiB0aGUgbWlzc2luZyB2YWx1ZXMgYXJlIG5vdCBsb2NhdGVkIGluIHNhbWUgcm93cywgdGhlIG1lYW4gaW1wdXRhdGlvbiB0byB0aGUgbnVtZXJpY2FsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgdXNlZC4NCg0KIyMJU2NhbiBJSQ0KDQoqIFNjYW4gb3V0bGllcnMgYnkgei1zY29yZSBtZXRob2QgKEFzIHRoZXJlIGFyZSB0b28gbWFueSBudW1lcmljYWwgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0IGFuZCB0aGUgbGVuZ3RoIGxpbWl0IG9mIDIwIHBhZ2VzIHJlcG9ydCwgdGhpcyBzZWN0aW9uIHdpbGwgb25seSBhaW0gdG8gc2lnbmlmaWNhbnQgbnVtZXJpY2FsIHZhcmlhYmxlcyBhYm91dCBwbGF5ZXJzJyBhYmlsaXRpZXMsIHdoaWNoIGFyZSAiT3ZlcmFsbCIsICJQb3RlbnRpYWwiLCAid2VpZ2h0IiwgYW5kICJhdmdfYWIiLg0KYGBge3J9DQojIEZvciAiT3ZlcmFsbCINCnouc2NvcmVfYWxsIDwtIGZpZmFfZmlsJE92ZXJhbGwgJT4lIHNjb3Jlcyh0eXBlID0gInoiKQ0Kei5zY29yZV9hbGwgJT4lIHN1bW1hcnkoKQ0KIyBWaXN1YWxpemF0aW9uDQpmaWZhX2ZpbCRPdmVyYWxsICU+JSBib3hwbG90KG1haW4gPSAiT3ZlcmFsbCIsIHlsYWIgPSAiUmF0aW5nIikNCmBgYA0KDQpgYGB7cn0NCiMgRmluZCB0b3RhbCBudW1iZXIgb2Ygb3V0bGllcnMNCmxlbmd0aCh3aGljaCggYWJzKHouc2NvcmVfYWxsKSA+IDMpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBEZWxldGUgdGhlIG91dGxpZXJzDQpjbGVhbl9vdmVyYWxsIDwtIGZpZmFfZmlsJE92ZXJhbGxbIC0gd2hpY2goIGFicyh6LnNjb3JlX2FsbCkgPiAzKV0NCmhlYWQoY2xlYW5fb3ZlcmFsbCkNCmBgYA0KDQpgYGB7cn0NCiMgRm9yICJQb2V0ZW50aWFsIg0Kei5zY29yZV9wb3QgPC0gZmlmYV9maWwkUG90ZW50aWFsICU+JSBzY29yZXModHlwZSA9ICJ6IikNCnouc2NvcmVfcG90ICU+JSBzdW1tYXJ5KCkNCiMgVmlzdWFsaXphdGlvbg0KZmlmYV9maWwkUG90ZW50aWFsICU+JSBib3hwbG90KG1haW4gPSAiUG90ZW50aWFsIiwgeWxhYiA9ICJSYXRpbmciKQ0KDQojIEZpbmQgdGhlIHRvdGFsIG51bWJlcnMgb2Ygb3V0bGllcnMNCmxlbmd0aCh3aGljaCggYWJzKHouc2NvcmVfcG90KT4gMykpDQpgYGANCg0KYGBge3J9DQojIERlbGV0ZSB0aGUgb3V0bGllcg0KY2xlYW5fcG90IDwtIGZpZmFfZmlsJFBvdGVudGlhbFsgLSB3aGljaCggYWJzKHouc2NvcmVfcG90KSA+IDMpXQ0KaGVhZChjbGVhbl9wb3QpDQpgYGANCg0KYGBge3J9DQojIEZvciAid2VpZ2h0Ig0Kei5zY29yZV93ZWkgPC0gZmlmYV9maWwkd2VpZ2h0ICU+JSBzY29yZXModHlwZSA9ICJ6IikNCnouc2NvcmVfd2VpICU+JSBzdW1tYXJ5KCkNCg0KIyBGaW5kIHRoZSB0b3RvYWwgbnVtYmVyIG9mIG91dGxpZXJzDQpsZW5ndGgod2hpY2goIGFicyh6LnNjb3JlX3dlaSk+IDMpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGb3IgYXZnX2FiDQp6LnNjb3JlX2F2Z2FiIDwtIGZpZmFfZmlsJGF2Z19hYiAlPiUgc2NvcmVzKHR5cGUgPSAieiIpDQp6LnNjb3JlX2F2Z2FiICU+JSBzdW1tYXJ5KCkNCg0KIyBGaW5kIHRoZSB0b3RvYWwgbnVtYmVyIG9mIG91dGxpZXJzDQpsZW5ndGgod2hpY2goIGFicyh6LnNjb3JlX2F2Z2FiKSA+IDMpKQ0KYGBgDQoNCkNhcHBpbmcsIG9uZSBvZiB0aGUgYXBwcm9hY2hlcyB0byBoYW5kbGluZyBvdXRsaWVycyB3aWxsIGJlIHVzZWQNCmBgYHtyfQ0KIyBEZWZpbmUgYSBmdW5jdGlvbiBvZiBjYXBwaW5nDQpjYXAgPC0gZnVuY3Rpb24oeCl7DQogIHF1YW50aWxlcyA8LSBxdWFudGlsZSggeCwgYyguMDUsIDAuMjUsIDAuNzUsIC45NSkpDQogIHhbIHggPCBxdWFudGlsZXNbMl0gLSAxLjUqSVFSKHgpXSA8LSBxdWFudGlsZXNbMV0NCiAgeFt4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KV0gPC0gcXVhbnRpbGVzWzRdDQogIHgNCn0NCmBgYA0KDQpgYGB7cn0NCmF2Z19hYl9jYXBwZWQgPC0gZmlmYV9maWwkYXZnX2FiICU+JSBjYXAoKQ0KaGVhZChhdmdfYWJfY2FwcGVkKQ0KYGBgDQoNCiMjCVRyYW5zZm9ybSANCkluIHRoaXMgc2VjdGlvbiwgdGhlIHZhcmlhYmxlIHdpbGwgYmUgdXNlZCB0byB0cmFuc2Zvcm1lZCBpcyAiT3ZlcmFsbCINCmBgYHtyfQ0KIyBHYWluIHRoZSBoaXN0b2dyYW0gb2YgdGhlIHZhcmlhYmxlIHRvIGRldGVybWluZSB0aGUgbWV0aG9kIG9mIHRyYW5zZm9ybWF0aW9uDQpoaXN0KGZpZmFfZmlsJE92ZXJhbGwpDQpgYGANClRoaXMgaXMgYSByaWdodC1za2V3ZWQgZGlzdHJpYnV0aW9uLCBtYW55IHRyYW5zZm9ybWF0aW9uIHdpbGwgYmUgdXNlZCB0byBjb21wYXJlLg0KDQoqIEJveC1Db3ggVHJhbnNmb3JtYXRpb24NCmBgYHtyfQ0KYm94Y294X292ZXJhbGwgPC0gQm94Q294KGZpZmFfZmlsJE92ZXJhbGwsIGxhbWJkYSA9ICJhdXRvIikNCg0KIyBDaGVjayB0aGUgcmVzdWx0DQpoaXN0KGJveGNveF9vdmVyYWxsKQ0KYGBgDQoNCiogTG9nYXJpdGhtaWMgdHJhbnNmb3JtYXRpb24NCmBgYHtyfQ0KbG9nX292ZWFsbCA8LSBsb2cxMChmaWZhX2ZpbCRPdmVyYWxsKQ0KIyBDaGVjayB0aGUgcmVzdWx0DQpoaXN0KGxvZ19vdmVhbGwpDQpgYGANCiogTmF0dXJhbCBsb2dhcml0aG0NCmBgYHtyfQ0KbG5fb3ZlcmFsbCA8LSBsb2coZmlmYV9maWwkT3ZlcmFsbCkNCg0KIyBDaGVjayB0aGUgcmVzdWx0DQpoaXN0KGxuX292ZXJhbGwpDQpgYGANCg0KKiBSZWNpcHJvY2FsIFRyYW5zZm9ybWF0aW9uDQpgYGB7cn0NCnJlY2lwX292ZXJhbGwgPC0gMS9maWZhX2ZpbCRPdmVyYWxsDQoNCiMgQ2hlY2sgdGhlIHJlc3VsdA0KaGlzdChyZWNpcF9vdmVyYWxsKQ0KYGBgDQpGcm9tIHRoZSByZXN1bHRzLCBpdCBjYW4gYmUgc2VlbiB0aGF0IHRoZSBib3gtY294IHRyYW5zZm9ybWF0aW9uIHdvcmtzIGJlc3Qgb24gdGhpcyBkYXRhLg0KDQoNCg0KDQo=