Required packages

library(readr)
library(dplyr)
library(tidyr)
library(forecast)
library(Hmisc)
library(outliers)
library(ggplot2)
library(editrules)

Executive Summary

The report involves two datasets that provides an overview about heroes and their physical as well as power characteristics.The data sets were merged together by using an appropriate matching column for performing preprocessing tasks on the final dataset.The original dataset describing the stats of the superheroes was subsetted to avoid duplicate columns.The structure of the dataset and variable class type was analysed and it was observed that there were 9 numerical and 8 character variables.Certain character variables were then converted into factors by defining the levels and assigning appropriate labels.The dataset conforms to the tidy data principles and is already in a tidy format so no tidy tasks were performed.A new variable ’ BMI’ was created using the mutate function that determines how fit a superhero is.The data set was then scanned for any missing values and special values along with any obvious errors or consistencies using appropriate functions.The missing values for categorical variables was classified as ‘unknown/not specified’ whereas the missing values in the numerical variables were imputed by the mean of the corresponding variables. The numeric variables in the dataset is scanned for outliers by using boxplot function and the outlier values are replaced using a cap function.The outlier values lying beyond the fences are replaced with the 5th or 95th percentile value. Finally for the transformation task,z-score standardization was performed on certain variables to make the distribution more normal and scale the data range. A linear relationship was established between the height and the weight of the superhero using a scatterplot.The graph showed a positive correlation between the two variables.

Data

Superheroes Dataset
The two datasets taken for performing data preprocessing contains information about different superheroes and their stats. The first dataset contains various information about the superhero’s appearance,their body measurements etc.There are about 734 observations and 10 variables in this dataset. Variable descriptions are as follows:
Name: Name of the superhero
Gender: Gender of the superhero
Eye color: Eye color of the superhero
Race: Species of the superhero
Height: Height of the superhero(in cms)
Publisher: Comic category of the superhero
Skin color: Skin color of the superhero
Alignment: Superhero’s nature.
Weight: Weight of the superhero(in kgs)

The second dataset records the superhero attributes such as their strength level,speed level etc.There are about 611 observations and 9 variables in the dataset. Variable descriptions are as follows:
Name: Name of the superhero
Alignment: Superhero’s nature.
Intelligence:Intelligence stats of the superhero
Strength:Strength stats of the superhero
Speed: Speed stats of the superhero
Durability: Durability stats of the superhero
Power: Power level of the superhero
Combat: Combat level of the superhero
Total: Total combined stats of the superhero

Data source:
https://www.kaggle.com/claudiodavi/superhero-set
https://www.kaggle.com/magshimimsummercamp/superheroes-info-and-stats#superheroes_stats.csv

The superhero stats dataset also contains the ‘alignment’ variable and hence has been excluded from the original dataset.The two datasets have been merged on the superhero name by using the mutating join function from the dplyr package bringing together all the details regarding the superheroes.

heroes_info <-read_csv("heroes_information.csv") 
head(heroes_info)
heroes_stats <- read_csv("superheroes_stats.csv")
head(heroes_stats)
heroes_stats1 <- subset(heroes_stats[,c(1,3:9)])
heroes_combined <- inner_join(heroes_info, heroes_stats1, by = "Name")
head(heroes_combined)

Understand

str(heroes_combined)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    600 obs. of  17 variables:
 $ Name        : chr  "A-Bomb" "Abe Sapien" "Abin Sur" "Abomination" ...
 $ Gender      : chr  "Male" "Male" "Male" "Male" ...
 $ Eye color   : chr  "yellow" "blue" "blue" "green" ...
 $ Race        : chr  "Human" "Icthyo Sapien" "Ungaran" "Human / Radiation" ...
 $ Hair color  : chr  "No Hair" "No Hair" "No Hair" "No Hair" ...
 $ Height      : num  203 191 185 203 -99 -99 185 178 191 188 ...
 $ Publisher   : chr  "Marvel Comics" "Dark Horse Comics" "DC Comics" "Marvel Comics" ...
 $ Skin color  : chr  "-" "blue" "red" "-" ...
 $ Alignment   : chr  "good" "good" "good" "bad" ...
 $ Weight      : num  441 65 90 441 -99 -99 88 81 104 108 ...
 $ Intelligence: num  38 88 50 63 88 63 NA 10 75 50 ...
 $ Strength    : num  100 14 90 80 100 10 NA 8 28 85 ...
 $ Speed       : num  17 35 53 53 83 12 NA 13 38 100 ...
 $ Durability  : num  80 42 64 90 99 100 NA 5 80 85 ...
 $ Power       : num  17 35 84 55 100 71 NA 5 72 100 ...
 $ Combat      : num  64 85 65 95 56 64 NA 20 95 40 ...
 $ Total       : num  316 299 406 436 526 320 NA 61 388 460 ...
table(sapply(heroes_combined, class))

character   numeric 
        8         9 
attributes(heroes_combined[1:17, ])
$row.names
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17

$class
[1] "tbl_df"     "tbl"        "data.frame"

$names
 [1] "Name"         "Gender"       "Eye color"    "Race"         "Hair color"   "Height"       "Publisher"    "Skin color"   "Alignment"   
[10] "Weight"       "Intelligence" "Strength"     "Speed"        "Durability"   "Power"        "Combat"       "Total"       
heroes_combined$Gender <- factor(heroes_combined$Gender,levels = c('Male','Female'))
heroes_combined$`Eye color` <- factor(heroes_combined$`Eye color`,levels = c('amber','black','blue','blue / white','brown','gold','green','green / blue','grey','hazel','indigo','purple','red','silver','violet','white','white / red','yellow'
                                                                      ,'yellow (without irises)','yellow / blue','yellow / red'),labels=c('amber','black','blue','blue&white','brown','gold','green','green & blue',    'grey','hazel','indigo','purple','red','silver','violet','white','white & red','yellow'
                                                                      ,'yellow(No iris)','yellow & blue','yellow & red'))
heroes_combined$Race <- factor(heroes_combined$Race,levels=c('Alien','Alpha','Amazon','Android','Animal','Asgardian','Atlantean','Bizarro','Bolovaxian','Clone','Cosmic Entity','Cyborg','Czarnian','Dathomirian Zabrak','Demi-God','Demon','Eternal','Flora Colossus','Frost Giant','God / Eternal','Gorilla','Gungan','Human','Human / Altered', 'Human / Clone','Human / Cosmic','Human / Radiation','Human-Kree','Human-Spartoi','Human-Vulcan','Human-Vuldarian','Icthyo Sapien','Inhuman','Kaiju','Kakarantharaian','Korugaran','Kryptonian','Luphomoid','Maiar','Martian','Metahuman','Mutant','Mutant / Clone','New God','Neyaphem','Parademon','Planet','Rodian','Saiyan','Spartoi','Strontian','Symbiote','Talokite','Tamaranean','Ungaran','Vampire','Xenomorph XX121','Yautja','Yoda species','Zen-Whoberian','Zombie'))
heroes_combined$`Hair color` <- factor(heroes_combined$`Hair color`,levels=c('Auburn','Black / Blue','Blond','Blue','Brown','Brown / Black','Brown / White','Gold','Green','Grey','Indigo','Magenta','No Hair','Orange','Orange / White','Pink','Purple','Red','Red / Grey','Red / Orange','Red / White','Silver','Strawberry Blond','White','Yellow'))
heroes_combined$Publisher <- factor(heroes_combined$Publisher,levels=c('ABC Studios','Dark Horse Comics','DC Comics','George Lucas','Hanna-Barbera','HarperCollins','Icon Comics','IDW Publishing','Image Comics','J. K. Rowling','J. R. R. Tolkien','Marvel Comics','Microsoft','NBC - Heroes','Rebellion','Shueisha','Sony Pictures','South Park','Star Trek','SyFy','Team Epic TV','Titan Books','Universal Studios','Wildstorm'))
heroes_combined$`Skin color` <- factor(heroes_combined$`Skin color`,levels=c('black','blue','blue-white','gold','gray','green','grey','orange','orange / white','pink','purple','red','red / black','silver','white','yellow'))  

heroes_combined$Alignment <- factor(heroes_combined$Alignment,levels=c('good','bad','neutral'))

str(heroes_combined)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    600 obs. of  17 variables:
 $ Name        : chr  "A-Bomb" "Abe Sapien" "Abin Sur" "Abomination" ...
 $ Gender      : Factor w/ 2 levels "Male","Female": 1 1 1 1 1 1 1 1 1 1 ...
 $ Eye color   : Factor w/ 21 levels "amber","black",..: 18 3 3 7 3 3 3 5 NA 3 ...
 $ Race        : Factor w/ 61 levels "Alien","Alpha",..: 23 32 55 27 11 NA 23 23 NA NA ...
 $ Hair color  : Factor w/ 25 levels "Auburn","Black / Blue",..: 13 13 13 13 NA 3 3 5 NA 24 ...
 $ Height      : num  203 191 185 203 -99 -99 185 178 191 188 ...
 $ Publisher   : Factor w/ 24 levels "ABC Studios",..: 12 2 3 12 12 14 3 12 12 12 ...
 $ Skin color  : Factor w/ 16 levels "black","blue",..: NA 2 12 NA NA NA NA NA NA NA ...
 $ Alignment   : Factor w/ 3 levels "good","bad","neutral": 1 1 1 2 2 1 1 1 1 2 ...
 $ Weight      : num  441 65 90 441 -99 -99 88 81 104 108 ...
 $ Intelligence: num  38 88 50 63 88 63 NA 10 75 50 ...
 $ Strength    : num  100 14 90 80 100 10 NA 8 28 85 ...
 $ Speed       : num  17 35 53 53 83 12 NA 13 38 100 ...
 $ Durability  : num  80 42 64 90 99 100 NA 5 80 85 ...
 $ Power       : num  17 35 84 55 100 71 NA 5 72 100 ...
 $ Combat      : num  64 85 65 95 56 64 NA 20 95 40 ...
 $ Total       : num  316 299 406 436 526 320 NA 61 388 460 ...

Tidy & Manipulate Data I

  1. Each variable must have its own column.
  2. Each observation must have its own row.
  3. Each value must have its own cell.
    From the dataset,we can observe that each of the rules have been satisfied and hence no tidy functions need to be applied on the dataset.

Tidy & Manipulate Data II

heroes_combined <- mutate(heroes_combined,BMI=heroes_combined$Weight/heroes_combined$Height)
head(heroes_combined)

Scan I

#Checking for NULL values in the dataset
colSums(is.na(heroes_combined))
        Name       Gender    Eye color         Race   Hair color       Height    Publisher   Skin color    Alignment       Weight Intelligence 
           0           24          139          299          271            0            7          556            3            0          170 
    Strength        Speed   Durability        Power       Combat        Total          BMI 
         169          171          172          172          170          172            0 
sum(is.na(heroes_combined))
[1] 2495
#Checking for infinite and NaN values
is.special <- function(x){
  if (is.numeric(x)) (is.infinite(x) | is.nan(x))
}
sapply(heroes_combined, function(x) sum(is.special(x)))
        Name       Gender    Eye color         Race   Hair color       Height    Publisher   Skin color    Alignment       Weight Intelligence 
           0            0            0            0            0            0            0            0            0            0            0 
    Strength        Speed   Durability        Power       Combat        Total          BMI 
           0            0            0            0            0            0            0 
#Checking for obvious errors and inconsistencies 
(Rule1 <- editset(c("Height >= 0","Weight >=0")))

Edit set:
num1 : 0 <= Height
num2 : 0 <= Weight 
Violated <- violatedEdits(Rule1,heroes_combined) 
summary(Violated)
Edit violations, 600 observations, 0 completely missing (0%):

Edit violations per record:
#Recoding -99 value as NAs
heroes_combined$Weight <- heroes_combined$Weight %>% na_if(-99)
heroes_combined$Height <- heroes_combined$Height %>% na_if(-99)
# Handling missing values for the variables
sum(is.na(heroes_combined$Gender))  
[1] 24
levels(heroes_combined$Gender) = c(levels(heroes_combined$Gender), "Not Specified")
heroes_combined$Gender[is.na(heroes_combined$Gender)] <- "Not Specified"

sum(is.na(heroes_combined$`Eye color`)) 
[1] 139
levels(heroes_combined$`Eye color`) = c(levels(heroes_combined$`Eye color`), "Unknown")
heroes_combined$`Eye color`[is.na(heroes_combined$`Eye color`)] <- "Unknown"

sum(is.na(heroes_combined$`Race`)) 
[1] 299
levels(heroes_combined$`Race`) = c(levels(heroes_combined$`Race`), "Unknown")
heroes_combined$`Race`[is.na(heroes_combined$`Race`)] <- "Unknown"

sum(is.na(heroes_combined$`Hair color`)) 
[1] 271
levels(heroes_combined$`Hair color`) = c(levels(heroes_combined$`Hair color`), "Unknown")
heroes_combined$`Hair color`[is.na(heroes_combined$`Hair color`)] <- "Unknown"

sum(is.na(heroes_combined$`Publisher`)) 
[1] 7
levels(heroes_combined$`Publisher`) = c(levels(heroes_combined$`Publisher`), "Unknown")
heroes_combined$`Publisher`[is.na(heroes_combined$`Publisher`)] <- "Unknown"

sum(is.na(heroes_combined$`Skin color`)) 
[1] 556
levels(heroes_combined$`Skin color`) = c(levels(heroes_combined$`Skin color`), "Unknown")
heroes_combined$`Skin color`[is.na(heroes_combined$`Skin color`)] <- "Unknown"

sum(is.na(heroes_combined$`Alignment`)) 
[1] 3
levels(heroes_combined$`Alignment`) = c(levels(heroes_combined$`Alignment`), "Unknown")
heroes_combined$`Alignment`[is.na(heroes_combined$`Alignment`)] <- "Unknown"

#Imputing the values with Mean for numeric variables
heroes_combined$Weight<-impute(heroes_combined$Weight,fun = mean)
heroes_combined$Height<-impute(heroes_combined$Height,fun = mean)
heroes_combined$Intelligence<-impute(heroes_combined$Intelligence,fun = mean)
heroes_combined$Strength<-impute(heroes_combined$Strength,fun = mean)
heroes_combined$Speed<-impute(heroes_combined$Speed,fun = mean)
heroes_combined$Durability<-impute(heroes_combined$Durability,fun = mean)
heroes_combined$Power<-impute(heroes_combined$Power,fun = mean)
heroes_combined$Combat<-impute(heroes_combined$Combat,fun = mean)
heroes_combined$Total<-impute(heroes_combined$Total,fun = mean)
heroes_combined$BMI<-impute(heroes_combined$BMI,fun = mean)

colSums(is.na(heroes_combined))
        Name       Gender    Eye color         Race   Hair color       Height    Publisher   Skin color    Alignment       Weight Intelligence 
           0            0            0            0            0            0            0            0            0            0            0 
    Strength        Speed   Durability        Power       Combat        Total          BMI 
           0            0            0            0            0            0            0 
sum(is.na(heroes_combined))
[1] 0

Scan II

boxplot(as.numeric(heroes_combined$Weight),main="Boxplot of Superhero's weight",ylab="Weight",col="grey")

boxplot(as.numeric(heroes_combined$Height),main="Boxplot of Superhero's height",ylab="Height",col="grey")

boxplot(as.numeric(heroes_combined$Intelligence),main="Boxplot of Superhero's intelligence",ylab="Intelligence",col="grey")

boxplot(as.numeric(heroes_combined$Strength),main="Boxplot of Superhero's strength",ylab="Strength",col="grey")

boxplot(as.numeric(heroes_combined$Speed),main="Boxplot of Superhero's speed",ylab="Speed",col="grey")

boxplot(as.numeric(heroes_combined$Durability),main="Boxplot of Superhero's durability",ylab="Durability",col="grey")

boxplot(as.numeric(heroes_combined$Power),main="Boxplot of Superhero's power",ylab="Power",col="grey")

boxplot(as.numeric(heroes_combined$Combat),main="Boxplot of Superhero's combat",ylab="Combat",col="grey")

boxplot(as.numeric(heroes_combined$Total),main="Boxplot of Superhero's total stats",ylab="Total stats",col="grey")

boxplot(as.numeric(heroes_combined$BMI),main="Boxplot of Superhero's BMI",ylab="BMI",col="grey")

# Capping outliers for the 7 numeric variables that have outliers

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
}
heroes_combined$Weight <- heroes_combined$Weight %>% cap()
boxplot(as.numeric(heroes_combined$Weight),main="Boxplot of Superhero's weight",ylab="Weight",col="blue")


heroes_combined$Height <- heroes_combined$Height %>% cap()
boxplot(as.numeric(heroes_combined$Height ),main="Boxplot of Superhero's height",ylab="Height",col="blue")


heroes_combined$Intelligence <- heroes_combined$Intelligence %>% cap()
boxplot(as.numeric(heroes_combined$Intelligence),main="Boxplot of Superhero's intelligence",ylab="Intelligence",col="blue")


heroes_combined$Speed <- heroes_combined$Speed %>% cap()
boxplot(as.numeric(heroes_combined$Speed),main="Boxplot of Superhero's Speed",ylab="Speed",col="blue")


heroes_combined$Combat <- heroes_combined$Combat %>% cap()
boxplot(as.numeric(heroes_combined$Combat),main="Boxplot of Superhero's Combat",ylab="Combat",col="blue")


heroes_combined$Total <- heroes_combined$Total %>% cap()
boxplot(as.numeric(heroes_combined$Total),main="Boxplot of Superhero's Total stats",ylab="Total Stats",col="blue")


heroes_combined$BMI <- heroes_combined$BMI %>% cap()
boxplot(as.numeric(heroes_combined$BMI),main="Boxplot of Superhero's BMI",ylab="BMI",col="blue")


#Summarizing the numeric variables after capping
summary(heroes_combined[, c(6, 10:18)])

 172 values imputed to 187.8131 


 180 values imputed to 114.2619 


 170 values imputed to 63.03721 


 169 values imputed to 41.35499 


 171 values imputed to 38.70629 


 172 values imputed to 59.89019 


 172 values imputed to 57.62617 


 170 values imputed to 60.77674 


 172 values imputed to 321.9346 

     Height          Weight       Intelligence       Strength          Speed         Durability         Power            Combat     
 Min.   :163.0   Min.   : 16.0   Min.   : 25.00   Min.   :  4.00   Min.   : 8.00   Min.   :  5.00   Min.   :  5.00   Min.   :28.00  
 1st Qu.:178.0   1st Qu.: 74.0   1st Qu.: 50.00   1st Qu.: 12.00   1st Qu.:25.00   1st Qu.: 42.00   1st Qu.: 43.00   1st Qu.:56.00  
 Median :187.8   Median :101.0   Median : 63.04   Median : 41.35   Median :38.71   Median : 59.89   Median : 57.63   Median :60.78  
 Mean   :184.5   Mean   :106.3   Mean   : 63.54   Mean   : 41.35   Mean   :38.32   Mean   : 59.89   Mean   : 57.63   Mean   :60.98  
 3rd Qu.:188.0   3rd Qu.:114.3   3rd Qu.: 75.00   3rd Qu.: 55.00   3rd Qu.:42.00   3rd Qu.: 80.00   3rd Qu.: 69.00   3rd Qu.:70.00  
 Max.   :211.0   Max.   :249.1   Max.   :100.00   Max.   :100.00   Max.   :83.00   Max.   :120.00   Max.   :100.00   Max.   :95.00  
     Total            BMI         
 Min.   :147.0   Min.   :-0.4648  
 1st Qu.:271.8   1st Qu.: 0.4000  
 Median :321.9   Median : 0.5277  
 Mean   :320.7   Mean   : 0.6718  
 3rd Qu.:359.0   3rd Qu.: 1.0000  
 Max.   :491.1   Max.   : 1.8182  

Transform

hist(heroes_combined$Height,main="Histogram of Superhero's Height")

centre_height <- scale(heroes_combined$Height, center = TRUE, scale = TRUE)
hist(centre_height,main="Histogram of Superhero's Height(Z-score Transformed)")


plot(heroes_combined$Height,heroes_combined$Weight, main="Superheroes Height v/s Weight",
   xlab=" Height ", ylab=" Weight ",pch=19,xlim=c(160,215))
abline(lm(heroes_combined$Weight~heroes_combined$Height), col="red") 
lines(lowess(heroes_combined$Height,heroes_combined$Weight), col = "blue")



LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMiwgMjAxOSIKYXV0aG9yOiAiQXNod2luIEFsZXggRmVybmFuZGVzIC0gUzM4MDM1ODE8YnI+IFN1bWVldCBDaGluZGFsaWEgLSBTMzc3NDY4NTxicj5BZGFyc2ggS3VtYXIgRGFzIC0gUzM3NjY1NzgiIApzdWJ0aXRsZTogQXNzaWdubWVudCAzCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQojIyBSZXF1aXJlZCBwYWNrYWdlcyAKYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KEhtaXNjKQpsaWJyYXJ5KG91dGxpZXJzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZWRpdHJ1bGVzKQpgYGAKIyMgRXhlY3V0aXZlIFN1bW1hcnkgClRoZSByZXBvcnQgaW52b2x2ZXMgdHdvIGRhdGFzZXRzIHRoYXQgcHJvdmlkZXMgYW4gb3ZlcnZpZXcgYWJvdXQgaGVyb2VzIGFuZCB0aGVpciBwaHlzaWNhbCBhcyB3ZWxsIGFzIHBvd2VyIGNoYXJhY3RlcmlzdGljcy5UaGUgZGF0YSBzZXRzIHdlcmUgbWVyZ2VkIHRvZ2V0aGVyIGJ5IHVzaW5nIGFuIGFwcHJvcHJpYXRlIG1hdGNoaW5nIGNvbHVtbiBmb3IgcGVyZm9ybWluZyBwcmVwcm9jZXNzaW5nIHRhc2tzIG9uIHRoZSBmaW5hbCBkYXRhc2V0LlRoZSBvcmlnaW5hbCBkYXRhc2V0IGRlc2NyaWJpbmcgdGhlIHN0YXRzIG9mIHRoZSBzdXBlcmhlcm9lcyB3YXMgc3Vic2V0dGVkIHRvIGF2b2lkIGR1cGxpY2F0ZSBjb2x1bW5zLlRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQgYW5kIHZhcmlhYmxlIGNsYXNzIHR5cGUgd2FzIGFuYWx5c2VkIGFuZCBpdCB3YXMgb2JzZXJ2ZWQgdGhhdCB0aGVyZSB3ZXJlIDkgbnVtZXJpY2FsIGFuZCA4IGNoYXJhY3RlciB2YXJpYWJsZXMuQ2VydGFpbiBjaGFyYWN0ZXIgdmFyaWFibGVzIHdlcmUgdGhlbiBjb252ZXJ0ZWQgaW50byBmYWN0b3JzIGJ5IGRlZmluaW5nIHRoZSBsZXZlbHMgYW5kIGFzc2lnbmluZyBhcHByb3ByaWF0ZSBsYWJlbHMuVGhlIGRhdGFzZXQgY29uZm9ybXMgdG8gdGhlIHRpZHkgZGF0YSBwcmluY2lwbGVzIGFuZCBpcyBhbHJlYWR5IGluIGEgdGlkeSBmb3JtYXQgc28gbm8gdGlkeSB0YXNrcyB3ZXJlIHBlcmZvcm1lZC5BIG5ldyB2YXJpYWJsZSAnIEJNSScgd2FzIGNyZWF0ZWQgdXNpbmcgdGhlIG11dGF0ZSBmdW5jdGlvbiB0aGF0IGRldGVybWluZXMgaG93IGZpdCBhIHN1cGVyaGVybyBpcy5UaGUgZGF0YSBzZXQgd2FzIHRoZW4gc2Nhbm5lZCBmb3IgYW55IG1pc3NpbmcgdmFsdWVzIGFuZCBzcGVjaWFsIHZhbHVlcyBhbG9uZyB3aXRoIGFueSBvYnZpb3VzIGVycm9ycyBvciBjb25zaXN0ZW5jaWVzIHVzaW5nIGFwcHJvcHJpYXRlIGZ1bmN0aW9ucy5UaGUgbWlzc2luZyB2YWx1ZXMgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3YXMgY2xhc3NpZmllZCBhcyAndW5rbm93bi9ub3Qgc3BlY2lmaWVkJyB3aGVyZWFzIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyB3ZXJlIGltcHV0ZWQgYnkgdGhlIG1lYW4gb2YgdGhlIGNvcnJlc3BvbmRpbmcgdmFyaWFibGVzLiAKVGhlIG51bWVyaWMgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0IGlzIHNjYW5uZWQgZm9yIG91dGxpZXJzIGJ5IHVzaW5nIGJveHBsb3QgZnVuY3Rpb24gYW5kIHRoZSBvdXRsaWVyIHZhbHVlcyBhcmUgcmVwbGFjZWQgdXNpbmcgYSBjYXAgZnVuY3Rpb24uVGhlIG91dGxpZXIgdmFsdWVzIGx5aW5nIGJleW9uZCB0aGUgZmVuY2VzIGFyZSByZXBsYWNlZCB3aXRoIHRoZSA1dGggb3IgOTV0aCBwZXJjZW50aWxlIHZhbHVlLiBGaW5hbGx5IGZvciB0aGUgdHJhbnNmb3JtYXRpb24gdGFzayx6LXNjb3JlIHN0YW5kYXJkaXphdGlvbiB3YXMgcGVyZm9ybWVkIG9uIGNlcnRhaW4gdmFyaWFibGVzIHRvIG1ha2UgdGhlIGRpc3RyaWJ1dGlvbiBtb3JlIG5vcm1hbCBhbmQgc2NhbGUgdGhlIGRhdGEgcmFuZ2UuIEEgbGluZWFyIHJlbGF0aW9uc2hpcCB3YXMgZXN0YWJsaXNoZWQgYmV0d2VlbiB0aGUgaGVpZ2h0IGFuZCB0aGUgd2VpZ2h0IG9mIHRoZSBzdXBlcmhlcm8gdXNpbmcgYSBzY2F0dGVycGxvdC5UaGUgZ3JhcGggc2hvd2VkIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcy4gCgojIyBEYXRhIAoqKlN1cGVyaGVyb2VzIERhdGFzZXQqKjxicj4KVGhlIHR3byBkYXRhc2V0cyB0YWtlbiBmb3IgcGVyZm9ybWluZyBkYXRhIHByZXByb2Nlc3NpbmcgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgZGlmZmVyZW50IHN1cGVyaGVyb2VzIGFuZCB0aGVpciBzdGF0cy4KVGhlIGZpcnN0IGRhdGFzZXQgY29udGFpbnMgdmFyaW91cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3VwZXJoZXJvJ3MgYXBwZWFyYW5jZSx0aGVpciBib2R5IG1lYXN1cmVtZW50cyBldGMuVGhlcmUgYXJlIGFib3V0IDczNCBvYnNlcnZhdGlvbnMgYW5kIDEwIHZhcmlhYmxlcyBpbiB0aGlzIGRhdGFzZXQuClZhcmlhYmxlIGRlc2NyaXB0aW9ucyBhcmUgYXMgZm9sbG93czo8YnI+Ck5hbWU6IE5hbWUgb2YgdGhlIHN1cGVyaGVybzxicj4KR2VuZGVyOiBHZW5kZXIgb2YgdGhlIHN1cGVyaGVybzxicj4KRXllIGNvbG9yOiBFeWUgY29sb3Igb2YgdGhlIHN1cGVyaGVybzxicj4KUmFjZTogU3BlY2llcyBvZiB0aGUgc3VwZXJoZXJvIDxicj4KSGVpZ2h0OiBIZWlnaHQgb2YgdGhlIHN1cGVyaGVybyhpbiBjbXMpPGJyPgpQdWJsaXNoZXI6IENvbWljIGNhdGVnb3J5IG9mIHRoZSBzdXBlcmhlcm8gPGJyPgpTa2luIGNvbG9yOiBTa2luIGNvbG9yIG9mIHRoZSBzdXBlcmhlcm8gPGJyPgpBbGlnbm1lbnQ6IFN1cGVyaGVybydzIG5hdHVyZS48YnI+CldlaWdodDogV2VpZ2h0IG9mIHRoZSBzdXBlcmhlcm8oaW4ga2dzKTxicj4KClRoZSBzZWNvbmQgZGF0YXNldCByZWNvcmRzIHRoZSBzdXBlcmhlcm8gYXR0cmlidXRlcyBzdWNoIGFzIHRoZWlyIHN0cmVuZ3RoIGxldmVsLHNwZWVkIGxldmVsIGV0Yy5UaGVyZSBhcmUgYWJvdXQgNjExIG9ic2VydmF0aW9ucyBhbmQgOSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuClZhcmlhYmxlIGRlc2NyaXB0aW9ucyBhcmUgYXMgZm9sbG93czo8YnI+Ck5hbWU6IE5hbWUgb2YgdGhlIHN1cGVyaGVybzxicj4KQWxpZ25tZW50OiBTdXBlcmhlcm8ncyBuYXR1cmUuPGJyPgpJbnRlbGxpZ2VuY2U6SW50ZWxsaWdlbmNlIHN0YXRzIG9mIHRoZSBzdXBlcmhlcm88YnI+ClN0cmVuZ3RoOlN0cmVuZ3RoIHN0YXRzIG9mIHRoZSBzdXBlcmhlcm8gPGJyPgpTcGVlZDogU3BlZWQgc3RhdHMgb2YgdGhlIHN1cGVyaGVybzxicj4KRHVyYWJpbGl0eTogRHVyYWJpbGl0eSBzdGF0cyBvZiB0aGUgc3VwZXJoZXJvIDxicj4KUG93ZXI6IFBvd2VyIGxldmVsIG9mIHRoZSBzdXBlcmhlcm8gPGJyPgpDb21iYXQ6IENvbWJhdCBsZXZlbCBvZiB0aGUgc3VwZXJoZXJvPGJyPgpUb3RhbDogVG90YWwgY29tYmluZWQgc3RhdHMgb2YgdGhlIHN1cGVyaGVybzxicj4KCipEYXRhIHNvdXJjZToqPGJyPiBodHRwczovL3d3dy5rYWdnbGUuY29tL2NsYXVkaW9kYXZpL3N1cGVyaGVyby1zZXQ8YnI+Cmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vbWFnc2hpbWltc3VtbWVyY2FtcC9zdXBlcmhlcm9lcy1pbmZvLWFuZC1zdGF0cyNzdXBlcmhlcm9lc19zdGF0cy5jc3Y8YnI+CgpUaGUgc3VwZXJoZXJvIHN0YXRzIGRhdGFzZXQgYWxzbyBjb250YWlucyB0aGUgJ2FsaWdubWVudCcgdmFyaWFibGUgYW5kIGhlbmNlIGhhcyBiZWVuIGV4Y2x1ZGVkIGZyb20gdGhlIG9yaWdpbmFsIGRhdGFzZXQuVGhlIHR3byBkYXRhc2V0cyBoYXZlIGJlZW4gbWVyZ2VkIG9uIHRoZSBzdXBlcmhlcm8gbmFtZSBieSB1c2luZyB0aGUgbXV0YXRpbmcgam9pbiBmdW5jdGlvbiBmcm9tIHRoZSBkcGx5ciBwYWNrYWdlIGJyaW5naW5nIHRvZ2V0aGVyIGFsbCB0aGUgZGV0YWlscyByZWdhcmRpbmcgdGhlIHN1cGVyaGVyb2VzLgpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmhlcm9lc19pbmZvIDwtcmVhZF9jc3YoImhlcm9lc19pbmZvcm1hdGlvbi5jc3YiKSAKaGVhZChoZXJvZXNfaW5mbykKaGVyb2VzX3N0YXRzIDwtIHJlYWRfY3N2KCJzdXBlcmhlcm9lc19zdGF0cy5jc3YiKQpoZWFkKGhlcm9lc19zdGF0cykKaGVyb2VzX3N0YXRzMSA8LSBzdWJzZXQoaGVyb2VzX3N0YXRzWyxjKDEsMzo5KV0pCmhlcm9lc19jb21iaW5lZCA8LSBpbm5lcl9qb2luKGhlcm9lc19pbmZvLCBoZXJvZXNfc3RhdHMxLCBieSA9ICJOYW1lIikKaGVhZChoZXJvZXNfY29tYmluZWQpCmBgYAoKIyMgVW5kZXJzdGFuZCAKKiBUaGUgc3RydWN0dXJlIGFuZCB0aGUgdHlwZSBvZiB2YXJpYWJsZSBoYXMgYmVlbiBpZGVudGlmaWVkIHVzaW5nIHRoZSAnc3RyJyBmdW5jdGlvbi5UaGUgZGF0YXNldCBjb21wcmlzZWQgb2YgOCBjaGFyYWN0ZXIgYW5kIDkgbnVtZXJpYyB2YXJpYWJsZXMuCkNlcnRhaW4gY2hhcmFjdGVyIHZhcmlhYmxlcyBzdWNoIGFzIGdlbmRlcixoYWlyIGNvbG9yIGV0Yy4gd2VyZSBjb252ZXJ0ZWQgaW50byBmYWN0b3JzIGJ5IGRlZmluaW5nIHRoZSBsZXZlbHMgYW5kIGFwcHJvcHJpYXRlIGxhYmVscyB1c2luZyB0aGUgZmFjdG9yIGZ1bmN0aW9uLgoKCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc3RyKGhlcm9lc19jb21iaW5lZCkKdGFibGUoc2FwcGx5KGhlcm9lc19jb21iaW5lZCwgY2xhc3MpKQphdHRyaWJ1dGVzKGhlcm9lc19jb21iaW5lZFsxOjE3LCBdKQpoZXJvZXNfY29tYmluZWQkR2VuZGVyIDwtIGZhY3RvcihoZXJvZXNfY29tYmluZWQkR2VuZGVyLGxldmVscyA9IGMoJ01hbGUnLCdGZW1hbGUnKSkKaGVyb2VzX2NvbWJpbmVkJGBFeWUgY29sb3JgIDwtIGZhY3RvcihoZXJvZXNfY29tYmluZWQkYEV5ZSBjb2xvcmAsbGV2ZWxzID0gYygnYW1iZXInLCdibGFjaycsJ2JsdWUnLCdibHVlIC8gd2hpdGUnLCdicm93bicsJ2dvbGQnLCdncmVlbicsJ2dyZWVuIC8gYmx1ZScsJ2dyZXknLCdoYXplbCcsJ2luZGlnbycsJ3B1cnBsZScsJ3JlZCcsJ3NpbHZlcicsJ3Zpb2xldCcsJ3doaXRlJywnd2hpdGUgLyByZWQnLCd5ZWxsb3cnCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsJ3llbGxvdyAod2l0aG91dCBpcmlzZXMpJywneWVsbG93IC8gYmx1ZScsJ3llbGxvdyAvIHJlZCcpLGxhYmVscz1jKCdhbWJlcicsJ2JsYWNrJywnYmx1ZScsJ2JsdWUmd2hpdGUnLCdicm93bicsJ2dvbGQnLCdncmVlbicsJ2dyZWVuICYgYmx1ZScsCSdncmV5JywnaGF6ZWwnLCdpbmRpZ28nLCdwdXJwbGUnLCdyZWQnLCdzaWx2ZXInLCd2aW9sZXQnLCd3aGl0ZScsJ3doaXRlICYgcmVkJywneWVsbG93JwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCd5ZWxsb3coTm8gaXJpcyknLCd5ZWxsb3cgJiBibHVlJywneWVsbG93ICYgcmVkJykpCmhlcm9lc19jb21iaW5lZCRSYWNlIDwtIGZhY3RvcihoZXJvZXNfY29tYmluZWQkUmFjZSxsZXZlbHM9YygnQWxpZW4nLCdBbHBoYScsJ0FtYXpvbicsJ0FuZHJvaWQnLCdBbmltYWwnLCdBc2dhcmRpYW4nLCdBdGxhbnRlYW4nLCdCaXphcnJvJywnQm9sb3ZheGlhbicsJ0Nsb25lJywnQ29zbWljIEVudGl0eScsJ0N5Ym9yZycsJ0N6YXJuaWFuJywnRGF0aG9taXJpYW4gWmFicmFrJywnRGVtaS1Hb2QnLCdEZW1vbicsJ0V0ZXJuYWwnLCdGbG9yYSBDb2xvc3N1cycsJ0Zyb3N0IEdpYW50JywnR29kIC8gRXRlcm5hbCcsJ0dvcmlsbGEnLCdHdW5nYW4nLCdIdW1hbicsJ0h1bWFuIC8gQWx0ZXJlZCcsICdIdW1hbiAvIENsb25lJywnSHVtYW4gLyBDb3NtaWMnLCdIdW1hbiAvIFJhZGlhdGlvbicsJ0h1bWFuLUtyZWUnLCdIdW1hbi1TcGFydG9pJywnSHVtYW4tVnVsY2FuJywnSHVtYW4tVnVsZGFyaWFuJywnSWN0aHlvIFNhcGllbicsJ0luaHVtYW4nLCdLYWlqdScsJ0tha2FyYW50aGFyYWlhbicsJ0tvcnVnYXJhbicsJ0tyeXB0b25pYW4nLCdMdXBob21vaWQnLCdNYWlhcicsJ01hcnRpYW4nLCdNZXRhaHVtYW4nLCdNdXRhbnQnLCdNdXRhbnQgLyBDbG9uZScsJ05ldyBHb2QnLCdOZXlhcGhlbScsJ1BhcmFkZW1vbicsJ1BsYW5ldCcsJ1JvZGlhbicsJ1NhaXlhbicsJ1NwYXJ0b2knLCdTdHJvbnRpYW4nLCdTeW1iaW90ZScsJ1RhbG9raXRlJywnVGFtYXJhbmVhbicsJ1VuZ2FyYW4nLCdWYW1waXJlJywnWGVub21vcnBoIFhYMTIxJywnWWF1dGphJywnWW9kYSBzcGVjaWVzJywnWmVuLVdob2JlcmlhbicsJ1pvbWJpZScpKQpoZXJvZXNfY29tYmluZWQkYEhhaXIgY29sb3JgIDwtIGZhY3RvcihoZXJvZXNfY29tYmluZWQkYEhhaXIgY29sb3JgLGxldmVscz1jKCdBdWJ1cm4nLCdCbGFjayAvIEJsdWUnLCdCbG9uZCcsJ0JsdWUnLCdCcm93bicsJ0Jyb3duIC8gQmxhY2snLCdCcm93biAvIFdoaXRlJywnR29sZCcsJ0dyZWVuJywnR3JleScsJ0luZGlnbycsJ01hZ2VudGEnLCdObyBIYWlyJywnT3JhbmdlJywnT3JhbmdlIC8gV2hpdGUnLCdQaW5rJywnUHVycGxlJywnUmVkJywnUmVkIC8gR3JleScsJ1JlZCAvIE9yYW5nZScsJ1JlZCAvIFdoaXRlJywnU2lsdmVyJywnU3RyYXdiZXJyeSBCbG9uZCcsJ1doaXRlJywnWWVsbG93JykpCmhlcm9lc19jb21iaW5lZCRQdWJsaXNoZXIgPC0gZmFjdG9yKGhlcm9lc19jb21iaW5lZCRQdWJsaXNoZXIsbGV2ZWxzPWMoJ0FCQyBTdHVkaW9zJywnRGFyayBIb3JzZSBDb21pY3MnLCdEQyBDb21pY3MnLCdHZW9yZ2UgTHVjYXMnLCdIYW5uYS1CYXJiZXJhJywnSGFycGVyQ29sbGlucycsJ0ljb24gQ29taWNzJywnSURXIFB1Ymxpc2hpbmcnLCdJbWFnZSBDb21pY3MnLCdKLiBLLiBSb3dsaW5nJywnSi4gUi4gUi4gVG9sa2llbicsJ01hcnZlbCBDb21pY3MnLCdNaWNyb3NvZnQnLCdOQkMgLSBIZXJvZXMnLCdSZWJlbGxpb24nLCdTaHVlaXNoYScsJ1NvbnkgUGljdHVyZXMnLCdTb3V0aCBQYXJrJywnU3RhciBUcmVrJywnU3lGeScsJ1RlYW0gRXBpYyBUVicsJ1RpdGFuIEJvb2tzJywnVW5pdmVyc2FsIFN0dWRpb3MnLCdXaWxkc3Rvcm0nKSkKaGVyb2VzX2NvbWJpbmVkJGBTa2luIGNvbG9yYCA8LSBmYWN0b3IoaGVyb2VzX2NvbWJpbmVkJGBTa2luIGNvbG9yYCxsZXZlbHM9YygnYmxhY2snLCdibHVlJywnYmx1ZS13aGl0ZScsJ2dvbGQnLCdncmF5JywnZ3JlZW4nLCdncmV5Jywnb3JhbmdlJywnb3JhbmdlIC8gd2hpdGUnLCdwaW5rJywncHVycGxlJywncmVkJywncmVkIC8gYmxhY2snLCdzaWx2ZXInLCd3aGl0ZScsJ3llbGxvdycpKSAgCgpoZXJvZXNfY29tYmluZWQkQWxpZ25tZW50IDwtIGZhY3RvcihoZXJvZXNfY29tYmluZWQkQWxpZ25tZW50LGxldmVscz1jKCdnb29kJywnYmFkJywnbmV1dHJhbCcpKQoKc3RyKGhlcm9lc19jb21iaW5lZCkKYGBgCgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoqIEZvciB0aGUgZGF0YXNldCB0byBiZSB0aWR5LHRoZSBkYXRhc2V0IHNob3VsZCBzYXRpc2Z5IHRoZSB0aWR5IGRhdGEgcHJpbmNpcGxlcyBpLmU8YnI+CjEuIEVhY2ggdmFyaWFibGUgbXVzdCBoYXZlIGl0cyBvd24gY29sdW1uLjxicj4KMi4gRWFjaCBvYnNlcnZhdGlvbiBtdXN0IGhhdmUgaXRzIG93biByb3cuPGJyPgozLiBFYWNoIHZhbHVlIG11c3QgaGF2ZSBpdHMgb3duIGNlbGwuPGJyPgpGcm9tIHRoZSBkYXRhc2V0LHdlIGNhbiBvYnNlcnZlIHRoYXQgZWFjaCBvZiB0aGUgcnVsZXMgaGF2ZSBiZWVuIHNhdGlzZmllZCBhbmQgaGVuY2Ugbm8gdGlkeSBmdW5jdGlvbnMgbmVlZCB0byBiZSBhcHBsaWVkIG9uIHRoZSBkYXRhc2V0LgpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpgYGAKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCgoqIEEgbmV3IHZhcmlhYmxlICdCTUknIGhhcyBiZWVuIGNyZWF0ZWQgZm9yIGVhY2ggc3VwZXJoZXJvIHdoaWNoIGludm9sdmVzIGRpdmlzaW9uIG9mIHdlaWdodCBieSBoZWlnaHQgdmFyaWFibGUuVGhlIEJNSSBkZXNjcmliZXMgaG93IGZpdCB0aGUgc3VwZXJoZXJvIGlzLiAKCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaGVyb2VzX2NvbWJpbmVkIDwtIG11dGF0ZShoZXJvZXNfY29tYmluZWQsQk1JPWhlcm9lc19jb21iaW5lZCRXZWlnaHQvaGVyb2VzX2NvbWJpbmVkJEhlaWdodCkKaGVhZChoZXJvZXNfY29tYmluZWQpCmBgYAoKCiMjCVNjYW4gSSAKKiBUaGUgZGF0YXNldCBpcyBzY2FubmVkIGZvciBhbnkgbWlzc2luZyB2YWx1ZXMsc3BlY2lhbCB2YWx1ZXMgYW5kIG9idmlvdXMgZXJyb3JzL2luY29uc2lzdGVuY2llcyBieSB1c2luZyBhcHByb3ByaWF0ZSBmdW5jdGlvbnMuV2Ugb2JzZXJ2ZSB0aGF0IHRoZXJlIGFyZSBtdWx0aXBsZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZmFjdG9yZWQgYW5kIG51bWVyaWMgdmFyaWFibGVzIHdpdGggdGhlIHRvdGFsIGNvdW50IGJlaW5nIDI0OS4gRnJvbSB0aGUgZGF0YXNldCxpdCBjYW4gYWxzbyBiZSBvYnNlcnZlZCB0aGF0IHRoZXJlIGFyZSBjZXJ0YWluIG1pc3NpbmcgdmFsdWVzIGNvZGVkIGFzIC05OSBpbiB0aGUgaGVpZ2h0IGFuZCB3ZWlnaHQgdmFyaWFibGVzLlRoZXNlIHZhbHVlcyBoYXZlIGJlZW4gY29udmVydGVkIGludG8gbnVsbCB2YWx1ZXMgYnkgdXNpbmcgdGhlIG5hX2lmIGZ1bmN0aW9uLgpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNDaGVja2luZyBmb3IgTlVMTCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQKY29sU3Vtcyhpcy5uYShoZXJvZXNfY29tYmluZWQpKQpzdW0oaXMubmEoaGVyb2VzX2NvbWJpbmVkKSkKCiNDaGVja2luZyBmb3IgaW5maW5pdGUgYW5kIE5hTiB2YWx1ZXMKaXMuc3BlY2lhbCA8LSBmdW5jdGlvbih4KXsKICBpZiAoaXMubnVtZXJpYyh4KSkgKGlzLmluZmluaXRlKHgpIHwgaXMubmFuKHgpKQp9CnNhcHBseShoZXJvZXNfY29tYmluZWQsIGZ1bmN0aW9uKHgpIHN1bShpcy5zcGVjaWFsKHgpKSkKYGBgCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI0NoZWNraW5nIGZvciBvYnZpb3VzIGVycm9ycyBhbmQgaW5jb25zaXN0ZW5jaWVzIAooUnVsZTEgPC0gZWRpdHNldChjKCJIZWlnaHQgPj0gMCIsIldlaWdodCA+PTAiKSkpClZpb2xhdGVkIDwtIHZpb2xhdGVkRWRpdHMoUnVsZTEsaGVyb2VzX2NvbWJpbmVkKSAKc3VtbWFyeShWaW9sYXRlZCkKCiNSZWNvZGluZyAtOTkgdmFsdWUgYXMgTkFzCmhlcm9lc19jb21iaW5lZCRXZWlnaHQgPC0gaGVyb2VzX2NvbWJpbmVkJFdlaWdodCAlPiUgbmFfaWYoLTk5KQpoZXJvZXNfY29tYmluZWQkSGVpZ2h0IDwtIGhlcm9lc19jb21iaW5lZCRIZWlnaHQgJT4lIG5hX2lmKC05OSkKYGBgCiogSW4gb3JkZXIgdG8gZGVhbCB3aXRoIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzLHRoZSBtaXNzaW5nIHZhbHVlcyBoYXZlIGJlZW4gY2xhc3NpZmllZCBhcyAndW5rbm93bi9ub3Qgc3BlY2lmaWVkJyBieSBkZWZpbmluZyBhIG5ldyBsZXZlbC5BcyBmb3IgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBudW1lcmljIHZhcmlhYmxlcyx0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGlzIHF1aXRlIGxhcmdlIGFuZCBleGNsdWRpbmcgdGhlIG1pc3NpbmcgdmFsdWVzIHdvdWxkIGNhdXNlIGRpc2NyZXBhbmNpZXMgaW4gdGhlIGRhdGFzZXQuIAoqIFRoZSBtaXNzaW5nIHZhbHVlcyBoYXZlIGJlZW4gaW1wdXRlZCB3aXRoIHRoZSBtZWFuIHZhbHVlIGZvciB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgaW4gdGhpcyBjYXNlIHVzaW5nIHRoZSBpbXB1dGUgZnVuY3Rpb24gZnJvbSB0aGUgJ2htaXNjJyBwYWNrYWdlIHNvIHRoYXQgdGhlcmUgaXMgY29uc2lzdGVuY3kgaW4gdGhlIGRhdGEuIFdlIHVzZSB0aGUgY29sc3Vtcyhpcy5uYSkgJiBzdW0oaXMubmEpIGZ1bmN0aW9uIHRvIHZlcmlmeSB0aGF0IHRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YXNldC4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBIYW5kbGluZyBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlIHZhcmlhYmxlcwpzdW0oaXMubmEoaGVyb2VzX2NvbWJpbmVkJEdlbmRlcikpICAKbGV2ZWxzKGhlcm9lc19jb21iaW5lZCRHZW5kZXIpID0gYyhsZXZlbHMoaGVyb2VzX2NvbWJpbmVkJEdlbmRlciksICJOb3QgU3BlY2lmaWVkIikKaGVyb2VzX2NvbWJpbmVkJEdlbmRlcltpcy5uYShoZXJvZXNfY29tYmluZWQkR2VuZGVyKV0gPC0gIk5vdCBTcGVjaWZpZWQiCgpzdW0oaXMubmEoaGVyb2VzX2NvbWJpbmVkJGBFeWUgY29sb3JgKSkgCmxldmVscyhoZXJvZXNfY29tYmluZWQkYEV5ZSBjb2xvcmApID0gYyhsZXZlbHMoaGVyb2VzX2NvbWJpbmVkJGBFeWUgY29sb3JgKSwgIlVua25vd24iKQpoZXJvZXNfY29tYmluZWQkYEV5ZSBjb2xvcmBbaXMubmEoaGVyb2VzX2NvbWJpbmVkJGBFeWUgY29sb3JgKV0gPC0gIlVua25vd24iCgpzdW0oaXMubmEoaGVyb2VzX2NvbWJpbmVkJGBSYWNlYCkpIApsZXZlbHMoaGVyb2VzX2NvbWJpbmVkJGBSYWNlYCkgPSBjKGxldmVscyhoZXJvZXNfY29tYmluZWQkYFJhY2VgKSwgIlVua25vd24iKQpoZXJvZXNfY29tYmluZWQkYFJhY2VgW2lzLm5hKGhlcm9lc19jb21iaW5lZCRgUmFjZWApXSA8LSAiVW5rbm93biIKCnN1bShpcy5uYShoZXJvZXNfY29tYmluZWQkYEhhaXIgY29sb3JgKSkgCmxldmVscyhoZXJvZXNfY29tYmluZWQkYEhhaXIgY29sb3JgKSA9IGMobGV2ZWxzKGhlcm9lc19jb21iaW5lZCRgSGFpciBjb2xvcmApLCAiVW5rbm93biIpCmhlcm9lc19jb21iaW5lZCRgSGFpciBjb2xvcmBbaXMubmEoaGVyb2VzX2NvbWJpbmVkJGBIYWlyIGNvbG9yYCldIDwtICJVbmtub3duIgoKc3VtKGlzLm5hKGhlcm9lc19jb21iaW5lZCRgUHVibGlzaGVyYCkpIApsZXZlbHMoaGVyb2VzX2NvbWJpbmVkJGBQdWJsaXNoZXJgKSA9IGMobGV2ZWxzKGhlcm9lc19jb21iaW5lZCRgUHVibGlzaGVyYCksICJVbmtub3duIikKaGVyb2VzX2NvbWJpbmVkJGBQdWJsaXNoZXJgW2lzLm5hKGhlcm9lc19jb21iaW5lZCRgUHVibGlzaGVyYCldIDwtICJVbmtub3duIgoKc3VtKGlzLm5hKGhlcm9lc19jb21iaW5lZCRgU2tpbiBjb2xvcmApKSAKbGV2ZWxzKGhlcm9lc19jb21iaW5lZCRgU2tpbiBjb2xvcmApID0gYyhsZXZlbHMoaGVyb2VzX2NvbWJpbmVkJGBTa2luIGNvbG9yYCksICJVbmtub3duIikKaGVyb2VzX2NvbWJpbmVkJGBTa2luIGNvbG9yYFtpcy5uYShoZXJvZXNfY29tYmluZWQkYFNraW4gY29sb3JgKV0gPC0gIlVua25vd24iCgpzdW0oaXMubmEoaGVyb2VzX2NvbWJpbmVkJGBBbGlnbm1lbnRgKSkgCmxldmVscyhoZXJvZXNfY29tYmluZWQkYEFsaWdubWVudGApID0gYyhsZXZlbHMoaGVyb2VzX2NvbWJpbmVkJGBBbGlnbm1lbnRgKSwgIlVua25vd24iKQpoZXJvZXNfY29tYmluZWQkYEFsaWdubWVudGBbaXMubmEoaGVyb2VzX2NvbWJpbmVkJGBBbGlnbm1lbnRgKV0gPC0gIlVua25vd24iCgojSW1wdXRpbmcgdGhlIHZhbHVlcyB3aXRoIE1lYW4gZm9yIG51bWVyaWMgdmFyaWFibGVzCmhlcm9lc19jb21iaW5lZCRXZWlnaHQ8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkV2VpZ2h0LGZ1biA9IG1lYW4pCmhlcm9lc19jb21iaW5lZCRIZWlnaHQ8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkSGVpZ2h0LGZ1biA9IG1lYW4pCmhlcm9lc19jb21iaW5lZCRJbnRlbGxpZ2VuY2U8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkSW50ZWxsaWdlbmNlLGZ1biA9IG1lYW4pCmhlcm9lc19jb21iaW5lZCRTdHJlbmd0aDwtaW1wdXRlKGhlcm9lc19jb21iaW5lZCRTdHJlbmd0aCxmdW4gPSBtZWFuKQpoZXJvZXNfY29tYmluZWQkU3BlZWQ8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkU3BlZWQsZnVuID0gbWVhbikKaGVyb2VzX2NvbWJpbmVkJER1cmFiaWxpdHk8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkRHVyYWJpbGl0eSxmdW4gPSBtZWFuKQpoZXJvZXNfY29tYmluZWQkUG93ZXI8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkUG93ZXIsZnVuID0gbWVhbikKaGVyb2VzX2NvbWJpbmVkJENvbWJhdDwtaW1wdXRlKGhlcm9lc19jb21iaW5lZCRDb21iYXQsZnVuID0gbWVhbikKaGVyb2VzX2NvbWJpbmVkJFRvdGFsPC1pbXB1dGUoaGVyb2VzX2NvbWJpbmVkJFRvdGFsLGZ1biA9IG1lYW4pCmhlcm9lc19jb21iaW5lZCRCTUk8LWltcHV0ZShoZXJvZXNfY29tYmluZWQkQk1JLGZ1biA9IG1lYW4pCgpjb2xTdW1zKGlzLm5hKGhlcm9lc19jb21iaW5lZCkpCnN1bShpcy5uYShoZXJvZXNfY29tYmluZWQpKQpgYGAKCiMjCVNjYW4gSUkKKiBJbiBvcmRlciB0byBjaGVjayBmb3Igb3V0bGllcnMgaW4gdGhlIGRhdGFzZXQsYm94cGxvdHMgaGF2ZSBiZWVuIHBsb3R0ZWQgZm9yIGVhY2ggbnVtZXJpYyB2YXJpYWJsZS5XaXRoIG1pc3NpbmcgdmFsdWVzIGhhbmRsZWQgZWZmZWN0aXZlbHkgaW4gdGhlIHByZXZpb3VzIHN0ZXAsdGhlIFR1a2V54oCZcyBtZXRob2Qgb2Ygb3V0bGllciBkZXRlY3Rpb24gY2FuIGJlIHVzZWQgdG8gZGV0ZWN0IGFueSBvdXRsaWVycyBpbiB0aGUgYm94cGxvdC5Gcm9tIHRoZSBib3hwbG90IG1ldGhvZCB3ZSBvYnNlcnZlIHRoYXQgdGhlcmUgYXJlIG91dGxpZXJzIGluIDcgbnVtZXJpYyB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuCiogVGhlIGNhcCBmdW5jdGlvbiBoYXMgYmVlbiB1c2VkIHRvIHJlcGxhY2UgdGhlIG91dGxpZXIgdmFsdWVzIHRoYXQgbGllIGJlbG93IHRoZSB2YWx1ZSBvZiB0aGUgbG93ZXIgZmVuY2UgKFExIC0gMS41IHggSVFSKSB3aXRoIHRoZSB2YWx1ZSBvZiA1dGggcGVyY2VudGlsZSBhbmQgcmVwbGFjZSB0aGUgb3V0bGllciB2YWx1ZXMgdGhhdCBsaWUgYWJvdmUgdGhlIHZhbHVlIG9mIHRoZSB1cHBlciBmZW5jZSAoUTMgKyAxLjUgeCBJUVIpIHdpdGggdGhlIHZhbHVlIG9mIHRoZSA5NXRoIHBlcmNlbnRpbGUuClN1bW1hcnkgZnVuY3Rpb24gaGFzIGJlZW4gdXNlZCB0byBjaGVjayB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHRoZSBudW1lcmljIHZhcmlhYmxlcyBiZWZvcmUgYW5kIGFmdGVyIGNhcHBpbmcuCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRXZWlnaHQpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3Mgd2VpZ2h0Iix5bGFiPSJXZWlnaHQiLGNvbD0iZ3JleSIpCmJveHBsb3QoYXMubnVtZXJpYyhoZXJvZXNfY29tYmluZWQkSGVpZ2h0KSxtYWluPSJCb3hwbG90IG9mIFN1cGVyaGVybydzIGhlaWdodCIseWxhYj0iSGVpZ2h0Iixjb2w9ImdyZXkiKQpib3hwbG90KGFzLm51bWVyaWMoaGVyb2VzX2NvbWJpbmVkJEludGVsbGlnZW5jZSksbWFpbj0iQm94cGxvdCBvZiBTdXBlcmhlcm8ncyBpbnRlbGxpZ2VuY2UiLHlsYWI9IkludGVsbGlnZW5jZSIsY29sPSJncmV5IikKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRTdHJlbmd0aCksbWFpbj0iQm94cGxvdCBvZiBTdXBlcmhlcm8ncyBzdHJlbmd0aCIseWxhYj0iU3RyZW5ndGgiLGNvbD0iZ3JleSIpCmJveHBsb3QoYXMubnVtZXJpYyhoZXJvZXNfY29tYmluZWQkU3BlZWQpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3Mgc3BlZWQiLHlsYWI9IlNwZWVkIixjb2w9ImdyZXkiKQpib3hwbG90KGFzLm51bWVyaWMoaGVyb2VzX2NvbWJpbmVkJER1cmFiaWxpdHkpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3MgZHVyYWJpbGl0eSIseWxhYj0iRHVyYWJpbGl0eSIsY29sPSJncmV5IikKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRQb3dlciksbWFpbj0iQm94cGxvdCBvZiBTdXBlcmhlcm8ncyBwb3dlciIseWxhYj0iUG93ZXIiLGNvbD0iZ3JleSIpCmJveHBsb3QoYXMubnVtZXJpYyhoZXJvZXNfY29tYmluZWQkQ29tYmF0KSxtYWluPSJCb3hwbG90IG9mIFN1cGVyaGVybydzIGNvbWJhdCIseWxhYj0iQ29tYmF0Iixjb2w9ImdyZXkiKQpib3hwbG90KGFzLm51bWVyaWMoaGVyb2VzX2NvbWJpbmVkJFRvdGFsKSxtYWluPSJCb3hwbG90IG9mIFN1cGVyaGVybydzIHRvdGFsIHN0YXRzIix5bGFiPSJUb3RhbCBzdGF0cyIsY29sPSJncmV5IikKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRCTUkpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3MgQk1JIix5bGFiPSJCTUkiLGNvbD0iZ3JleSIpCmBgYAoKYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIENhcHBpbmcgb3V0bGllcnMgZm9yIHRoZSA3IG51bWVyaWMgdmFyaWFibGVzIHRoYXQgaGF2ZSBvdXRsaWVycwoKY2FwIDwtIGZ1bmN0aW9uKHgpewogIHF1YW50aWxlcyA8LSBxdWFudGlsZSggeCwgYyguMDUsIDAuMjUsIDAuNzUsIC45NSApICkKICB4WyB4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1sxXQogIHhbIHggPiBxdWFudGlsZXNbM10gKyAxLjUqSVFSKHgpIF0gPC0gcXVhbnRpbGVzWzRdCiAgeAp9Cmhlcm9lc19jb21iaW5lZCRXZWlnaHQgPC0gaGVyb2VzX2NvbWJpbmVkJFdlaWdodCAlPiUgY2FwKCkKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRXZWlnaHQpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3Mgd2VpZ2h0Iix5bGFiPSJXZWlnaHQiLGNvbD0iYmx1ZSIpCgpoZXJvZXNfY29tYmluZWQkSGVpZ2h0IDwtIGhlcm9lc19jb21iaW5lZCRIZWlnaHQgJT4lIGNhcCgpCmJveHBsb3QoYXMubnVtZXJpYyhoZXJvZXNfY29tYmluZWQkSGVpZ2h0ICksbWFpbj0iQm94cGxvdCBvZiBTdXBlcmhlcm8ncyBoZWlnaHQiLHlsYWI9IkhlaWdodCIsY29sPSJibHVlIikKCmhlcm9lc19jb21iaW5lZCRJbnRlbGxpZ2VuY2UgPC0gaGVyb2VzX2NvbWJpbmVkJEludGVsbGlnZW5jZSAlPiUgY2FwKCkKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRJbnRlbGxpZ2VuY2UpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3MgaW50ZWxsaWdlbmNlIix5bGFiPSJJbnRlbGxpZ2VuY2UiLGNvbD0iYmx1ZSIpCgpoZXJvZXNfY29tYmluZWQkU3BlZWQgPC0gaGVyb2VzX2NvbWJpbmVkJFNwZWVkICU+JSBjYXAoKQpib3hwbG90KGFzLm51bWVyaWMoaGVyb2VzX2NvbWJpbmVkJFNwZWVkKSxtYWluPSJCb3hwbG90IG9mIFN1cGVyaGVybydzIFNwZWVkIix5bGFiPSJTcGVlZCIsY29sPSJibHVlIikKCmhlcm9lc19jb21iaW5lZCRDb21iYXQgPC0gaGVyb2VzX2NvbWJpbmVkJENvbWJhdCAlPiUgY2FwKCkKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRDb21iYXQpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3MgQ29tYmF0Iix5bGFiPSJDb21iYXQiLGNvbD0iYmx1ZSIpCgpoZXJvZXNfY29tYmluZWQkVG90YWwgPC0gaGVyb2VzX2NvbWJpbmVkJFRvdGFsICU+JSBjYXAoKQpib3hwbG90KGFzLm51bWVyaWMoaGVyb2VzX2NvbWJpbmVkJFRvdGFsKSxtYWluPSJCb3hwbG90IG9mIFN1cGVyaGVybydzIFRvdGFsIHN0YXRzIix5bGFiPSJUb3RhbCBTdGF0cyIsY29sPSJibHVlIikKCmhlcm9lc19jb21iaW5lZCRCTUkgPC0gaGVyb2VzX2NvbWJpbmVkJEJNSSAlPiUgY2FwKCkKYm94cGxvdChhcy5udW1lcmljKGhlcm9lc19jb21iaW5lZCRCTUkpLG1haW49IkJveHBsb3Qgb2YgU3VwZXJoZXJvJ3MgQk1JIix5bGFiPSJCTUkiLGNvbD0iYmx1ZSIpCgojU3VtbWFyaXppbmcgdGhlIG51bWVyaWMgdmFyaWFibGVzIGFmdGVyIGNhcHBpbmcKc3VtbWFyeShoZXJvZXNfY29tYmluZWRbLCBjKDYsIDEwOjE4KV0pCgpgYGAKCgojIwlUcmFuc2Zvcm0gCgoqIFotc2NvcmUgdHJhbnNmb3JtYXRpb25zIGhhdmUgYmVlbiBhcHBsaWVkIG9uIHRoZSBIZWlnaHQgdmFyaWFibGUgc28gdGhhdCB0aGUgdmFsdWVzIGFyZSBjZW50cmVkIHRvd2FyZHMgdGhlIG1lYW4gYW5kIGFyZSBzY2FsZWQgZG93biB0byBhICBjb21tb24gcmFuZ2UgaW4gb3JkZXIgdG8gbm9ybWFsaXplIHRoZSBkYXRhLiBUaGUgc2NhbGUgZnVuY3Rpb24gaXMgdXNlZCB0byBwZXJmb3JtIHRoZSB6LXNjb3JlIHRyYW5zZm9ybWF0aW9uIHdpdGggY2VudGVyIGFuZCBzY2FsZSBhcmd1bWVudHMgc2V0IGFzIHRydWUuIAoqIEEgbGluZWFyIHJlbGF0aW9uc2hpcCBjYW4gYmUgb2JzZXJ2ZWQgYmV0d2VlbiB0aGUgaGVpZ2h0IGFuZCB3ZWlnaHQgb2YgdGhlIHN1cGVyaGVybyBieSBwbG90dGluZyBhIHNjYXR0ZXJwbG90LkZyb20gdGhlIGdyYXBoLHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBhIHBvc2l0aXZlIGNvLXJlbGF0aW9uIGJldHdlZW4gaGVpZ2h0IGFuZCB3ZWlnaHQgb2YgdGhlIHN1cGVyaGVyby4gIApgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cmhpc3QoaGVyb2VzX2NvbWJpbmVkJEhlaWdodCxtYWluPSJIaXN0b2dyYW0gb2YgU3VwZXJoZXJvJ3MgSGVpZ2h0IikKY2VudHJlX2hlaWdodCA8LSBzY2FsZShoZXJvZXNfY29tYmluZWQkSGVpZ2h0LCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCmhpc3QoY2VudHJlX2hlaWdodCxtYWluPSJIaXN0b2dyYW0gb2YgU3VwZXJoZXJvJ3MgSGVpZ2h0KFotc2NvcmUgVHJhbnNmb3JtZWQpIikKCnBsb3QoaGVyb2VzX2NvbWJpbmVkJEhlaWdodCxoZXJvZXNfY29tYmluZWQkV2VpZ2h0LCBtYWluPSJTdXBlcmhlcm9lcyBIZWlnaHQgdi9zIFdlaWdodCIsCiAgIHhsYWI9IiBIZWlnaHQgIiwgeWxhYj0iIFdlaWdodCAiLHBjaD0xOSx4bGltPWMoMTYwLDIxNSkpCmFibGluZShsbShoZXJvZXNfY29tYmluZWQkV2VpZ2h0fmhlcm9lc19jb21iaW5lZCRIZWlnaHQpLCBjb2w9InJlZCIpIApsaW5lcyhsb3dlc3MoaGVyb2VzX2NvbWJpbmVkJEhlaWdodCxoZXJvZXNfY29tYmluZWQkV2VpZ2h0KSwgY29sID0gImJsdWUiKQoKYGBgCgoKCgo8YnI+Cjxicj4K