This notebook uses the Data Science Survey on Kaggle dataset to understand the tools, preferred language and commonly used algorithms of data science practitioners in various working fields.
Load libraries
library(tidyverse)
library(ggdark)
library(viridis)
library(ggsci)
library(skimr)
Import data
data = read.csv("kagglesurvey.csv")
dim(data)
[1] 10153 5
head(data)
- 10153 survey responses in the dataset
str(data)
'data.frame': 10153 obs. of 5 variables:
$ Respondent : int 1 2 3 4 5 6 7 8 9 10 ...
$ WorkToolsSelect : chr "Amazon Web services,Oracle Data Mining/ Oracle R Enterprise,Perl" "Amazon Machine Learning,Amazon Web services,Cloudera,Hadoop/Hive/Pig,Impala,Java,Mathematica,MATLAB/Octave,Micr"| __truncated__ "C/C++,Jupyter notebooks,MATLAB/Octave,Python,R,TensorFlow" "Jupyter notebooks,Python,SQL,TensorFlow" ...
$ LanguageRecommendationSelect: chr "F#" "Python" "Python" "Python" ...
$ EmployerIndustry : chr "Internet-based" "Mix of fields" "Technology" "Academic" ...
$ WorkAlgorithmsSelect : chr "Neural Networks,Random Forests,RNNs" "Bayesian Techniques,Decision Trees,Random Forests,Regression/Logistic Regression" "Bayesian Techniques,CNNs,Ensemble Methods,Neural Networks,Regression/Logistic Regression,SVMs" "Bayesian Techniques,CNNs,Decision Trees,Gradient Boosted Machines,Neural Networks,Random Forests,Regression/Log"| __truncated__ ...
Dataset features:
- Respondent: id
- WorkToolsSelect: Tools used
- LanguageRecommendationSelect: Preferred Language
- EmployerIndustry: Working fields
- WorkAlgorithmsSelect: Algorithm commonly used by respondents
Missing data
# convert blanks to NA
data1 = mutate_all(data, list(~na_if(.,"")))
# missing values
#sapply(data1, function(x) sum(is.na(x)))
skim(data1)
── Data Summary ────────────────────────
Values
Name data1
Number of rows 8132
Number of columns 7
_______________________
Column type frequency:
character 4
numeric 3
________________________
Group variables None
── Variable type: character ───────────────────────────────────────────────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max empty n_unique whitespace
1 WorkToolsSelect 177 0.978 1 834 0 5248 0
2 LanguageRecommendationSelect 1598 0.803 1 8 0 13 0
3 EmployerIndustry 29 0.996 5 32 0 16 0
4 WorkAlgorithmsSelect 831 0.898 4 216 0 1420 0
── Variable type: numeric ─────────────────────────────────────────────────────────────────────────────────────────────────
skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
1 Respondent 0 1 4515. 2844. 1 2035. 4244. 6922. 10153 ▇▇▆▆▅
2 wt_counts 0 1 5.56 3.47 0 3 5 7 49 ▇▁▁▁▁
3 alg_count 0 1 3.28 2.43 0 1 3 5 15 ▇▅▁▁▁
# number of complete cases
data1 %>% filter(complete.cases(.)) %>% tally()
# drop obs with blanks across all columns except for ID
incomplete_df = data %>% filter(WorkToolsSelect=="", LanguageRecommendationSelect =="", EmployerIndustry =="", WorkAlgorithmsSelect =="")
cdf = anti_join(data, incomplete_df, by="Respondent")
dim(cdf)
[1] 9027 5
# drop obs with blanks in WorkToolsSelect, LanguageRecommendationSelect and WorkAlgorithmsSelect
incomplete_df2 = cdf %>% filter(WorkToolsSelect=="", LanguageRecommendationSelect =="", WorkAlgorithmsSelect =="")
cdf2 = anti_join(cdf, incomplete_df2, by="Respondent")
dim(cdf2)
[1] 8132 5
data = cdf2
- Out of 10153 observations, there are:
- 5991 complete cases
- 9027 obs have no blanks across all columns except for ID
- of which, 8132 obs have no blanks in WorkToolsSelect, LanguageRecommendationSelect and WorkAlgorithmsSelect
- The following sections uses the subset containing 8132 obs.
Preferred language
#plot
data %>% group_by(LanguageRecommendationSelect) %>% tally() %>% mutate_if(is.character,list(~na_if(.,""))) %>% ggplot(aes(x=reorder(LanguageRecommendationSelect,n), y=n)) + geom_col(width=0.8) + dark_theme_minimal() + labs(x="", y="Count", title="Preferred language") + coord_flip()

length(unique(data$LanguageRecommendationSelect))
[1] 14
# table (all levels)
data %>% group_by(LanguageRecommendationSelect) %>% tally(sort=T) %>% mutate_if(is.character,list(~na_if(.,""))) %>% mutate(prop=round(n/sum(n),3))
# table (excluding NA level)
data %>% filter(LanguageRecommendationSelect !="") %>% group_by(LanguageRecommendationSelect) %>% tally(sort=T) %>% mutate(prop=round(n/sum(n),3))
- 14 levels in the variable LanguageReccomendationSelect (preferred language), including one NA level
- 1598 out of 8132 respondents (19.7%) did not specify any language of preference
- of those that specified languages: 62% prefer Python, 25.6% prefer R and 4.2% prefer SQL
Working field
# plot
data %>% group_by(EmployerIndustry) %>% tally() %>% mutate_if(is.character,list(~na_if(.,""))) %>% ggplot(aes(x=reorder(EmployerIndustry,n), y=n)) + geom_col() + dark_theme_minimal() + labs(x="", y="Count", title="Working field") + coord_flip()

# summary table
length(unique(data$EmployerIndustry))
[1] 17
data %>% group_by(EmployerIndustry) %>% tally(sort=T) %>% mutate_if(is.character,list(~na_if(.,""))) %>% mutate(prop=round(n/sum(n),3))
- 29 out of 8132 respondents did not specify a working field
- Majority of the respondents (~46%) work in Technology, Academic or Financial fields.
Count of algorithms per response
data$alg_count = lengths(strsplit(data$WorkAlgorithmsSelect,","))
summary(data$alg_count)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 1.000 3.000 3.277 5.000 15.000
# plot
data %>% group_by(alg_count) %>% tally() %>% ggplot(aes(x=alg_count, y=n)) + geom_col(width=0.8) + dark_theme_minimal() + labs(x="Number of algorithms per response", y="Count")

# summary table
data %>% group_by(alg_count) %>% tally() %>% mutate(prop=round(n/sum(n),3))
- Median of 3 algorithms and maximum of 15 algorithms listed
- 831 out of 8132 responses (10.2%) did not list any commonly used algorithms
- Around 62% of the respondents listed one to four commonly used algorithms
Algorithms user counts
# spilt then flatten
alg <- data %>%
mutate(WorkAlgorithmsSelect =str_split(WorkAlgorithmsSelect,",") ) %>%
unnest(WorkAlgorithmsSelect)
# unique levels
length(unique(alg$WorkAlgorithmsSelect))
[1] 16
# table
alg1 = alg %>% group_by(WorkAlgorithmsSelect) %>% tally(sort=T) %>% mutate_if(is.character,list(~na_if(.,"")))
alg1
# plot
alg1 %>% ggplot(aes(x=reorder(WorkAlgorithmsSelect,n), y=n)) + geom_col(width=0.8) + dark_theme_minimal() + labs(x="", y="Count", title="Commonly used Algorithms") + coord_flip()

- 16 unique levels in the variable WorkAlgorithmsSelect, including one NA level
- Three Most frequent commonly used algorithms listed are
- Regression/Logistic Regression (n=4636)
- Decision Trees (n=3460)
- Random Forest (n=3378)
- Least frequent commonly used algorithms listed by respondents is GANS (n=207)
Most frequent commonly used algorithms in each industry
# proportion of most frequent algorithm in respective industry
alg %>% filter(EmployerIndustry != "") %>% filter(WorkAlgorithmsSelect != "") %>% group_by(EmployerIndustry, WorkAlgorithmsSelect) %>% tally() %>% mutate(prop=round(n/sum(n),3)) %>% arrange(desc(prop), .by_group=TRUE) %>% group_by(EmployerIndustry) %>% slice(1) %>% as.data.frame()
- Regression/Logistic regression is the most frequent algorithm commonly used across working fields, except for Military/Security field where Neural Network is the most frequent.
# LanguageRecommendationSelect by industry
data %>% filter(EmployerIndustry != "") %>% filter(LanguageRecommendationSelect != "") %>% group_by(EmployerIndustry, LanguageRecommendationSelect) %>% tally() %>% mutate(prop=n/sum(n)) %>% arrange(desc(prop), .by_group=TRUE) %>% group_by(EmployerIndustry) %>% slice(1) %>% as.data.frame()
# plot
data %>% filter(LanguageRecommendationSelect !="") %>% filter(EmployerIndustry !="") %>% ggplot(aes(x=LanguageRecommendationSelect, y= fct_rev(EmployerIndustry), color=LanguageRecommendationSelect)) + geom_point() + theme_minimal() + labs(y="",x="") + scale_color_simpsons() + theme(legend.position="none", plot.background = element_rect(fill = "white"), panel.border = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank())

- Python, R and SQL are listed as respondents’ preferred language across all(16) working fields.
LS0tCnRpdGxlOiAiRGF0YSBTY2llbmNlIFN1cnZleSBFREEiCmRhdGU6ICJEZWMgMjAyMCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBub3RlYm9vayB1c2VzIHRoZSBbRGF0YSBTY2llbmNlIFN1cnZleSBvbiBLYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20va2luZ2FienByby9kYXRhc2NpZW5jZS1zdXJ2ZXktb24ta2FnZ2xlKSBkYXRhc2V0IHRvIHVuZGVyc3RhbmQgdGhlIHRvb2xzLCBwcmVmZXJyZWQgbGFuZ3VhZ2UgYW5kIGNvbW1vbmx5IHVzZWQgYWxnb3JpdGhtcyBvZiBkYXRhIHNjaWVuY2UgcHJhY3RpdGlvbmVycyBpbiB2YXJpb3VzIHdvcmtpbmcgZmllbGRzLiAKCgojIyMgTG9hZCBsaWJyYXJpZXMgCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ2RhcmspCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShnZ3NjaSkKbGlicmFyeShza2ltcikKYGBgCgojIyMgSW1wb3J0IGRhdGEKYGBge3J9CmRhdGEgPSByZWFkLmNzdigia2FnZ2xlc3VydmV5LmNzdiIpCmRpbShkYXRhKQpoZWFkKGRhdGEpCmBgYAoKKiAxMDE1MyBzdXJ2ZXkgcmVzcG9uc2VzIGluIHRoZSBkYXRhc2V0CgpgYGB7cn0Kc3RyKGRhdGEpCmBgYAoKRGF0YXNldCBmZWF0dXJlczogCgoqIFJlc3BvbmRlbnQ6IGlkCiogV29ya1Rvb2xzU2VsZWN0OiBUb29scyB1c2VkCiogTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdDogUHJlZmVycmVkIExhbmd1YWdlCiogRW1wbG95ZXJJbmR1c3RyeTogV29ya2luZyBmaWVsZHMgCiogV29ya0FsZ29yaXRobXNTZWxlY3Q6IEFsZ29yaXRobSBjb21tb25seSB1c2VkIGJ5IHJlc3BvbmRlbnRzIAoKCiMjIyBNaXNzaW5nIGRhdGEgCmBgYHtyfQojIGNvbnZlcnQgYmxhbmtzIHRvIE5BCmRhdGExID0gbXV0YXRlX2FsbChkYXRhLCBsaXN0KH5uYV9pZiguLCIiKSkpCiMgbWlzc2luZyB2YWx1ZXMgCiNzYXBwbHkoZGF0YTEsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpIApza2ltKGRhdGExKQojIG51bWJlciBvZiBjb21wbGV0ZSBjYXNlcwpkYXRhMSAlPiUgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pKSAlPiUgdGFsbHkoKQpgYGAKCmBgYHtyfQojIGRyb3Agb2JzIHdpdGggYmxhbmtzIGFjcm9zcyBhbGwgY29sdW1ucyBleGNlcHQgZm9yIElECmluY29tcGxldGVfZGYgPSBkYXRhICU+JSBmaWx0ZXIoV29ya1Rvb2xzU2VsZWN0PT0iIiwgTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdCA9PSIiLCBFbXBsb3llckluZHVzdHJ5ID09IiIsIFdvcmtBbGdvcml0aG1zU2VsZWN0ID09IiIpIApjZGYgPSBhbnRpX2pvaW4oZGF0YSwgaW5jb21wbGV0ZV9kZiwgYnk9IlJlc3BvbmRlbnQiKQpkaW0oY2RmKSAKIyBkcm9wIG9icyB3aXRoIGJsYW5rcyBpbiBXb3JrVG9vbHNTZWxlY3QsIExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QgYW5kIFdvcmtBbGdvcml0aG1zU2VsZWN0CmluY29tcGxldGVfZGYyID0gY2RmICU+JSBmaWx0ZXIoV29ya1Rvb2xzU2VsZWN0PT0iIiwgTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdCA9PSIiLCBXb3JrQWxnb3JpdGhtc1NlbGVjdCA9PSIiKSAKY2RmMiA9IGFudGlfam9pbihjZGYsIGluY29tcGxldGVfZGYyLCBieT0iUmVzcG9uZGVudCIpCmRpbShjZGYyKQpkYXRhID0gY2RmMgpgYGAKCiogT3V0IG9mIDEwMTUzIG9ic2VydmF0aW9ucywgdGhlcmUgYXJlOiAKICArIDU5OTEgY29tcGxldGUgY2FzZXMKICArIDkwMjcgb2JzIGhhdmUgbm8gYmxhbmtzIGFjcm9zcyBhbGwgY29sdW1ucyBleGNlcHQgZm9yIElECiAgICArIG9mIHdoaWNoLCA4MTMyIG9icyBoYXZlIG5vIGJsYW5rcyBpbiBXb3JrVG9vbHNTZWxlY3QsIExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QgYW5kIFdvcmtBbGdvcml0aG1zU2VsZWN0IAoqIFRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgdXNlcyB0aGUgc3Vic2V0IGNvbnRhaW5pbmcgODEzMiBvYnMuIAoKCiMjIyBUb29scyB1c2VkCmBgYHtyfQojIHNwaWx0IHN0cmluZyB0aGVuIGZsYXR0ZW4gIAp0b29scyA8LSBkYXRhICAlPiUgCiAgICBtdXRhdGUod29ya190b29scyA9c3RyX3NwbGl0KFdvcmtUb29sc1NlbGVjdCwiLCIpICkgICU+JSAKICAgIHVubmVzdCh3b3JrX3Rvb2xzKQojIG51bWJlciBvZiB0b29scyBsaXN0ZWQKbGVuZ3RoKHVuaXF1ZSh0b29scyR3b3JrX3Rvb2xzKSkKYGBgCgojIyMjIFVzZXIgY291bnQgb2YgZWFjaCB0b29sCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NX0KIyBwbG90IChieSBhbHBoYWJldGljYWwgb3JkZXIpCnVjX3Rvb2wgPSB0b29scyAlPiUgZ3JvdXBfYnkod29ya190b29scykgJT4lIHRhbGx5KHNvcnQ9VCkgJT4lIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsbGlzdCh+bmFfaWYoLiwiIikpKQp1Y190b29sICU+JSBtdXRhdGUod29ya190b29scyA9IGZjdF9yZXYod29ya190b29scykpICU+JSBnZ3Bsb3QoYWVzKHg9d29ya190b29scywgeT1uLCBmaWxsPW4pKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKyBkYXJrX3RoZW1lX21pbmltYWwoKSArIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb249ImNpdmlkaXMiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgbGFicyh4PSIiLHk9IlVzZXIgQ291bnQiLCB0aXRsZT0iVXNlciBjb3VudCBvZiBlYWNoIHRvb2wiKQpgYGAKCiogVG9vbHMgd2l0aCBoaWdoZXN0IHVzZXIgY291bnQ6IFB5dGhvbiA+IFIgPiBTUUwgPiBKdXB5dGVyIG5vdGVib29rcyAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQojIDUgbW9zdCBjb21tb24gdG9vbHMgYnkgdXNlciBjb3VudAp0b29scyAlPiUgZ3JvdXBfYnkod29ya190b29scykgJT4lIHRhbGx5KHNvcnQ9VCkgJT4lIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsbGlzdCh+bmFfaWYoLiwiIikpKSAlPiUgdG9wX24oNSkKIyA1IGxlYXN0IGNvbW1vbiB0b29scyBieSB1c2VyIGNvdW50CnRvb2xzICU+JSBncm91cF9ieSh3b3JrX3Rvb2xzKSAlPiUgdGFsbHkoc29ydD1UKSAlPiUgbXV0YXRlX2lmKGlzLmNoYXJhY3RlcixsaXN0KH5uYV9pZiguLCIiKSkpICU+JSB0b3BfbigtNSkKYGBgCgoqIFRoZXJlIGFyZSA0OSB1bmlxdWUgdG9vbHMgbGlzdGVkIGluIHRoZSBkYXRhc2V0LCBhbmQgb25lIE5BIGxldmVsLiAKKiBUb29scyB3aXRoIHRoZSBoaWdoZXN0IHVzZXIgY291bnQ6IFB5dGhvbiA+IFIgPiBTUUwgPiBKdXB5dGVyIG5vdGVib29rcyA+IFRlbnNvcmZsb3cuCiogTGVhc3QgZnJlcXVlbnQgdG9vbHMgYnkgdXNlciBjb3VudCBhcmUgRGF0YVJvYm90LCBTdGF0aXN0aWNhLCBLTklNRSwgU2FsZnJvZCBTeXN0ZW1zIGFuZCBBbmdyb3NzLiAKCiMjIyMgTnVtYmVyIG9mIHRvb2xzIHVzZWQgcGVyIHJlc3BvbmRlbnQKCmBgYHtyfQojIGNvdW50IG9mIHRvb2xzIHBlciByZXNwb25kZW50IApkYXRhJHd0X2NvdW50cyA9IGxlbmd0aHMoc3Ryc3BsaXQoZGF0YSRXb3JrVG9vbHNTZWxlY3QsIiwiKSkKc3VtbWFyeShkYXRhJHd0X2NvdW50cykKCiMgdGFibGUgCmN0b29scyA9IGRhdGEgJT4lIGdyb3VwX2J5KHd0X2NvdW50cykgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wPXJvdW5kKG4vc3VtKG4pLDMpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCmhlYWQoY3Rvb2xzKQoKIyBwbG90CmRhdGEgJT4lIGdyb3VwX2J5KHd0X2NvdW50cykgJT4lIHRhbGx5KCkgICU+JSBnZ3Bsb3QoYWVzKHg9d3RfY291bnRzLCB5PW4pKSArIGdlb21fY29sKCkgKyBkYXJrX3RoZW1lX21pbmltYWwoKSArIGxhYnMoeD0iTnVtYmVyIG9mIHRvb2xzIiwgeT0iQ291bnQiLCB0aXRsZT0iTnVtYmVyIG9mIHRvb2xzIGxpc3RlZCBieSByZXNwb25kZW50cyIpCgojIGNvdW50IG9mIHRvb2xzIHBlciByZXNwb25kZW50IChleGNsdWRpbmcgYmxhbmtzKQpkYXRhX3RyPSBkYXRhICU+JSBmaWx0ZXIod3RfY291bnRzIT0wKQpzdW1tYXJ5KGRhdGFfdHIkd3RfY291bnRzKQoKIyB0YWJsZSAoZXhjbHVkZSBOQSkKY3Rvb2xzMiA9IGRhdGFfdHIgJT4lIGdyb3VwX2J5KHd0X2NvdW50cykgJT4lIHRhbGx5KHNvcnQ9VCkgJT4lIG11dGF0ZShwcm9wPXJvdW5kKG4vc3VtKG4pLDIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCmhlYWQoY3Rvb2xzMikKYGBgCgoqIDE3NyBvdXQgb2YgODEzMiByZXNwb25kZW50cyBkaWQgbm90IHNwZWNpZnkgYW55IHRvb2xzCiogT2YgdGhvc2UgdGhhdCBzcGVjaWZpZWQgb25lIG9yIG1vcmUgdG9vbHMgKGluIFdvcmtUb29scyBTZWxlY3QpCiAgKiBtZWRpYW4gb2YgNSB0b29scyBsaXN0ZWQgaW4gYSByZXNwb25zZQogICogYXJvdW5kIDUxJSBvZiB0aGUgcmVzcG9uZGVudHMgbGlzdGVkIHRocmVlIHRvIHNpeCB3b3JrIHRvb2xzCgoKIyMjIFByZWZlcnJlZCBsYW5ndWFnZQoKYGBge3J9CiNwbG90CmRhdGEgJT4lIGdyb3VwX2J5KExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QpICU+JSB0YWxseSgpICU+JSBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLGxpc3Qofm5hX2lmKC4sIiIpKSkgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QsbiksIHk9bikpICsgZ2VvbV9jb2wod2lkdGg9MC44KSArIGRhcmtfdGhlbWVfbWluaW1hbCgpICsgbGFicyh4PSIiLCB5PSJDb3VudCIsIHRpdGxlPSJQcmVmZXJyZWQgbGFuZ3VhZ2UiKSArIGNvb3JkX2ZsaXAoKQpgYGAKCgpgYGB7cn0KbGVuZ3RoKHVuaXF1ZShkYXRhJExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QpKSAKIyB0YWJsZSAoYWxsIGxldmVscykKZGF0YSAlPiUgZ3JvdXBfYnkoTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdCkgJT4lIHRhbGx5KHNvcnQ9VCkgJT4lIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsbGlzdCh+bmFfaWYoLiwiIikpKSAlPiUgbXV0YXRlKHByb3A9cm91bmQobi9zdW0obiksMykpCiMgdGFibGUgKGV4Y2x1ZGluZyBOQSBsZXZlbCkKZGF0YSAlPiUgZmlsdGVyKExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QgIT0iIikgJT4lIGdyb3VwX2J5KExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QpICU+JSB0YWxseShzb3J0PVQpICU+JSBtdXRhdGUocHJvcD1yb3VuZChuL3N1bShuKSwzKSkKYGBgCgoqIDE0IGxldmVscyBpbiB0aGUgdmFyaWFibGUgTGFuZ3VhZ2VSZWNjb21lbmRhdGlvblNlbGVjdCAocHJlZmVycmVkIGxhbmd1YWdlKSwgaW5jbHVkaW5nIG9uZSBOQSBsZXZlbAoqIDE1OTggb3V0IG9mIDgxMzIgcmVzcG9uZGVudHMgKDE5LjclKSBkaWQgbm90IHNwZWNpZnkgYW55IGxhbmd1YWdlIG9mIHByZWZlcmVuY2UKKiBvZiB0aG9zZSB0aGF0IHNwZWNpZmllZCBsYW5ndWFnZXM6IDYyJSBwcmVmZXIgUHl0aG9uLCAyNS42JSBwcmVmZXIgUiBhbmQgIDQuMiUgcHJlZmVyIFNRTCAgCgoKIyMjIFdvcmtpbmcgZmllbGQKYGBge3J9CiMgcGxvdApkYXRhICU+JSBncm91cF9ieShFbXBsb3llckluZHVzdHJ5KSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlX2lmKGlzLmNoYXJhY3RlcixsaXN0KH5uYV9pZiguLCIiKSkpICU+JSBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihFbXBsb3llckluZHVzdHJ5LG4pLCB5PW4pKSArIGdlb21fY29sKCkgKyBkYXJrX3RoZW1lX21pbmltYWwoKSArIGxhYnMoeD0iIiwgeT0iQ291bnQiLCB0aXRsZT0iV29ya2luZyBmaWVsZCIpICsgY29vcmRfZmxpcCgpCmBgYAoKYGBge3J9CiMgc3VtbWFyeSB0YWJsZQpsZW5ndGgodW5pcXVlKGRhdGEkRW1wbG95ZXJJbmR1c3RyeSkpIApkYXRhICU+JSBncm91cF9ieShFbXBsb3llckluZHVzdHJ5KSAlPiUgdGFsbHkoc29ydD1UKSAlPiUgbXV0YXRlX2lmKGlzLmNoYXJhY3RlcixsaXN0KH5uYV9pZiguLCIiKSkpICU+JSBtdXRhdGUocHJvcD1yb3VuZChuL3N1bShuKSwzKSkKYGBgCgoqIDI5IG91dCBvZiA4MTMyIHJlc3BvbmRlbnRzIGRpZCBub3Qgc3BlY2lmeSBhIHdvcmtpbmcgZmllbGQgCiogTWFqb3JpdHkgb2YgdGhlIHJlc3BvbmRlbnRzICh+NDYlKSB3b3JrIGluIFRlY2hub2xvZ3ksIEFjYWRlbWljIG9yIEZpbmFuY2lhbCBmaWVsZHMuIAoKIyMjIENvdW50IG9mIGFsZ29yaXRobXMgcGVyIHJlc3BvbnNlCmBgYHtyfQpkYXRhJGFsZ19jb3VudCA9IGxlbmd0aHMoc3Ryc3BsaXQoZGF0YSRXb3JrQWxnb3JpdGhtc1NlbGVjdCwiLCIpKQpzdW1tYXJ5KGRhdGEkYWxnX2NvdW50KQojIHBsb3QKZGF0YSAlPiUgZ3JvdXBfYnkoYWxnX2NvdW50KSAlPiUgdGFsbHkoKSAlPiUgZ2dwbG90KGFlcyh4PWFsZ19jb3VudCwgeT1uKSkgKyBnZW9tX2NvbCh3aWR0aD0wLjgpICsgZGFya190aGVtZV9taW5pbWFsKCkgKyBsYWJzKHg9Ik51bWJlciBvZiBhbGdvcml0aG1zIHBlciByZXNwb25zZSIsIHk9IkNvdW50IikKIyBzdW1tYXJ5IHRhYmxlCmRhdGEgJT4lIGdyb3VwX2J5KGFsZ19jb3VudCkgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wPXJvdW5kKG4vc3VtKG4pLDMpKQpgYGAKCiogTWVkaWFuIG9mIDMgYWxnb3JpdGhtcyBhbmQgbWF4aW11bSBvZiAxNSBhbGdvcml0aG1zIGxpc3RlZAoqIDgzMSBvdXQgb2YgODEzMiByZXNwb25zZXMgKDEwLjIlKSBkaWQgbm90IGxpc3QgYW55IGNvbW1vbmx5IHVzZWQgYWxnb3JpdGhtcyAKKiBBcm91bmQgNjIlIG9mIHRoZSByZXNwb25kZW50cyBsaXN0ZWQgb25lIHRvIGZvdXIgY29tbW9ubHkgdXNlZCBhbGdvcml0aG1zIAoKCiMjIyBBbGdvcml0aG1zIHVzZXIgY291bnRzIApgYGB7cn0KIyBzcGlsdCB0aGVuIGZsYXR0ZW4gIAphbGcgPC0gZGF0YSAgJT4lIAogICAgbXV0YXRlKFdvcmtBbGdvcml0aG1zU2VsZWN0ID1zdHJfc3BsaXQoV29ya0FsZ29yaXRobXNTZWxlY3QsIiwiKSApICAlPiUgCiAgICB1bm5lc3QoV29ya0FsZ29yaXRobXNTZWxlY3QpCiMgdW5pcXVlIGxldmVscwpsZW5ndGgodW5pcXVlKGFsZyRXb3JrQWxnb3JpdGhtc1NlbGVjdCkpCiMgdGFibGUKYWxnMSA9IGFsZyAlPiUgZ3JvdXBfYnkoV29ya0FsZ29yaXRobXNTZWxlY3QpICU+JSB0YWxseShzb3J0PVQpICU+JSBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLGxpc3Qofm5hX2lmKC4sIiIpKSkgCmFsZzEKYGBgCgpgYGB7cn0KIyBwbG90CmFsZzEgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKFdvcmtBbGdvcml0aG1zU2VsZWN0LG4pLCB5PW4pKSArIGdlb21fY29sKHdpZHRoPTAuOCkgKyBkYXJrX3RoZW1lX21pbmltYWwoKSArIGxhYnMoeD0iIiwgeT0iQ291bnQiLCB0aXRsZT0iQ29tbW9ubHkgdXNlZCBBbGdvcml0aG1zIikgKyBjb29yZF9mbGlwKCkKYGBgCgoqIDE2IHVuaXF1ZSBsZXZlbHMgaW4gdGhlIHZhcmlhYmxlIFdvcmtBbGdvcml0aG1zU2VsZWN0LCBpbmNsdWRpbmcgb25lIE5BIGxldmVsCiogVGhyZWUgTW9zdCBmcmVxdWVudCBjb21tb25seSB1c2VkIGFsZ29yaXRobXMgbGlzdGVkIGFyZSAKICArIFJlZ3Jlc3Npb24vTG9naXN0aWMgUmVncmVzc2lvbiAobj00NjM2KQogICsgRGVjaXNpb24gVHJlZXMgKG49MzQ2MCkKICArIFJhbmRvbSBGb3Jlc3QgKG49MzM3OCkKKiBMZWFzdCBmcmVxdWVudCBjb21tb25seSB1c2VkIGFsZ29yaXRobXMgbGlzdGVkIGJ5IHJlc3BvbmRlbnRzIGlzIEdBTlMgKG49MjA3KQoKCiMjIyBNb3N0IGZyZXF1ZW50IHRvb2wgaW4gZWFjaCBpbmR1c3RyeQpgYGB7cn0KIyBwcm9wb3J0aW9uIG9mIG1vc3QgZnJlcXVlbnQgdG9vbHMgaW4gcmVzcGVjdGl2ZSBpbmR1c3RyeSAKdG9vbHMgJT4lIGZpbHRlcihFbXBsb3llckluZHVzdHJ5ICE9ICIiKSAlPiUgZmlsdGVyKHdvcmtfdG9vbHMgIT0gIiIpICU+JSBncm91cF9ieShFbXBsb3llckluZHVzdHJ5LCB3b3JrX3Rvb2xzKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9cm91bmQobi9zdW0obiksMykpICU+JSBhcnJhbmdlKGRlc2MocHJvcCksIC5ieV9ncm91cD1UUlVFKSAlPiUgZ3JvdXBfYnkoRW1wbG95ZXJJbmR1c3RyeSkgJT4lIHNsaWNlKDEpICU+JSBhcy5kYXRhLmZyYW1lKCkKYGBgCgoqIFB5dGhvbiBpcyB0aGUgbW9zdCBmcmVxdWVudCB0b29sIHVzZWQgYnkgcmVzcG9uZGVudHMgYWNyb3NzIHRoZSB2YXJpb3VzIHdvcmtpbmcgZmllbGRzLCBleGNlcHQgZm9yIEluc3VyYW5jZSBhbmQgTm9uX3Byb2ZpdCB3aGVyZSBSIGlzIHRoZSBtb3N0IGZyZXF1ZW50IHRvb2wgdXNlZC4gCgojIyMgTW9zdCBmcmVxdWVudCBjb21tb25seSB1c2VkIGFsZ29yaXRobXMgaW4gZWFjaCBpbmR1c3RyeQpgYGB7cn0KIyBwcm9wb3J0aW9uIG9mIG1vc3QgZnJlcXVlbnQgYWxnb3JpdGhtIGluIHJlc3BlY3RpdmUgaW5kdXN0cnkgCmFsZyAlPiUgZmlsdGVyKEVtcGxveWVySW5kdXN0cnkgIT0gIiIpICU+JSBmaWx0ZXIoV29ya0FsZ29yaXRobXNTZWxlY3QgIT0gIiIpICU+JSBncm91cF9ieShFbXBsb3llckluZHVzdHJ5LCBXb3JrQWxnb3JpdGhtc1NlbGVjdCkgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wPXJvdW5kKG4vc3VtKG4pLDMpKSAlPiUgYXJyYW5nZShkZXNjKHByb3ApLCAuYnlfZ3JvdXA9VFJVRSkgJT4lIGdyb3VwX2J5KEVtcGxveWVySW5kdXN0cnkpICU+JSBzbGljZSgxKSAlPiUgYXMuZGF0YS5mcmFtZSgpCmBgYAoKKiBSZWdyZXNzaW9uL0xvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgdGhlIG1vc3QgZnJlcXVlbnQgYWxnb3JpdGhtIGNvbW1vbmx5IHVzZWQgYWNyb3NzIHdvcmtpbmcgZmllbGRzLCBleGNlcHQgZm9yIE1pbGl0YXJ5L1NlY3VyaXR5IGZpZWxkIHdoZXJlIE5ldXJhbCBOZXR3b3JrIGlzIHRoZSBtb3N0IGZyZXF1ZW50LiAKCmBgYHtyfQojIExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QgYnkgaW5kdXN0cnkKZGF0YSAlPiUgZmlsdGVyKEVtcGxveWVySW5kdXN0cnkgIT0gIiIpICU+JSBmaWx0ZXIoTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdCAhPSAiIikgJT4lIGdyb3VwX2J5KEVtcGxveWVySW5kdXN0cnksIExhbmd1YWdlUmVjb21tZW5kYXRpb25TZWxlY3QpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGFycmFuZ2UoZGVzYyhwcm9wKSwgLmJ5X2dyb3VwPVRSVUUpICU+JSBncm91cF9ieShFbXBsb3llckluZHVzdHJ5KSAlPiUgc2xpY2UoMSkgJT4lIGFzLmRhdGEuZnJhbWUoKQoKIyBwbG90IApkYXRhICU+JSBmaWx0ZXIoTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdCAhPSIiKSAlPiUgZmlsdGVyKEVtcGxveWVySW5kdXN0cnkgIT0iIikgJT4lIGdncGxvdChhZXMoeD1MYW5ndWFnZVJlY29tbWVuZGF0aW9uU2VsZWN0LCB5PSBmY3RfcmV2KEVtcGxveWVySW5kdXN0cnkpLCBjb2xvcj1MYW5ndWFnZVJlY29tbWVuZGF0aW9uU2VsZWN0KSkgKyBnZW9tX3BvaW50KCkgKyB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHk9IiIseD0iIikgKyBzY2FsZV9jb2xvcl9zaW1wc29ucygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAKCmBgYAoqIFB5dGhvbiwgUiBhbmQgU1FMIGFyZSBsaXN0ZWQgYXMgcmVzcG9uZGVudHMnIHByZWZlcnJlZCBsYW5ndWFnZSBhY3Jvc3MgYWxsKDE2KSB3b3JraW5nIGZpZWxkcy4gCgojIyMgUG9ycG9ydGlvbiBvZiBSICh3b3JrX3Rvb2wpIGluIGVhY2ggRW1wbG95ZXJJbmR1c3RyeQpgYGB7cn0KIyB0YWJsZSAKdG9vbHMgJT4lIGZpbHRlcihFbXBsb3llckluZHVzdHJ5ICE9ICIiKSAlPiUgZmlsdGVyKHdvcmtfdG9vbHMgIT0gIiIpICU+JSBncm91cF9ieShFbXBsb3llckluZHVzdHJ5LCB3b3JrX3Rvb2xzKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9cm91bmQobi9zdW0obiksMykpICU+JSBhcnJhbmdlKGRlc2MocHJvcCksIC5ieV9ncm91cD1UUlVFKSAlPiUgZ3JvdXBfYnkoRW1wbG95ZXJJbmR1c3RyeSkgJT4lIGZpbHRlcih3b3JrX3Rvb2xzPT0iUiIpICU+JSBhcnJhbmdlKGRlc2MocHJvcCkpCmBgYApUaGUgZmllbGRzIHRoYXQgaGF2ZSB0aGUgaGlnaGVzdCBwcm9wb3J0aW9uIG9mIFIgKHdvcmsgdG9vbCkgYXJlIG5vbi1wcm9maXQsIGluc3VyYW5jZSBhbmQgZ292ZXJubWVudCwgcmVzcGVjdGl2ZWx5LiAKCiMjIyBUb29scyB1c2VkIGluIHByZWZlcnJlZCBsYW5ndWFnZXMgZ3JvdXBzIFIgYW5kIFB5dGhvbgpgYGB7cn0KIyB0YWJsZSAKdG9vbHMgJT4lIGZpbHRlcihMYW5ndWFnZVJlY29tbWVuZGF0aW9uU2VsZWN0ID09ICJSIikgJT4lIGdyb3VwX2J5KHdvcmtfdG9vbHMpICU+JSB0YWxseShzb3J0PVQpICU+JSBzbGljZSgxOjEwKQojIHRhYmxlCnRvb2xzICU+JSBmaWx0ZXIoTGFuZ3VhZ2VSZWNvbW1lbmRhdGlvblNlbGVjdCA9PSAiUHl0aG9uIikgJT4lIGdyb3VwX2J5KHdvcmtfdG9vbHMpICU+JSB0YWxseShzb3J0PVQpICU+JSBzbGljZSgxOjEwKQpgYGAKCiogVGhlcmUgYXJlIHNvbWUgZGlmZmVyZW5jZXMgaW4gbW9zdCBmcmVxdWVudCB0b29scyB1c2VkIGJ5IHRoZSB0d28gZ3JvdXBzIAogICsgUiAocHJlZmVycmVkIGxhbmd1YWdlKSBncm91cDogbW9zdCBmcmVxdWVudCB3b3JrIHRvb2xzIHVzZWQgYXJlIFB5dGhvbiwgU1FMLCBUYWJsZWF1IGFuZCBKdXB5dGVyIE5vdGVib29rLCByZXNwZWN0aXZlbHkKICArIFB5dGhvbiAocHJlZmVycmVkIGxhbmd1YWdlKSBncm91cDogbW9zdCBmcmVxdWVudCB3b3JrIHRvb2xzIHVzZWQgYXJlIFNRTCwgSnVweXRlciBOb3RlYm9vaywgUiBhbmQgVGVuc29yRmxvdywgcmVzcGVjdGl2ZWx5CgoKIyMjIEhvdyBtYW55IHJlc3BvbmRlbnRzIHVzZSBib3RoIFIgYW5kIFB5dGhvbiAod29yayB0b29scykgPyBhbmQgd2hhdCBvdGhlciB3b3JrIHRvb2xzIHRoZXNlIHJlc3BvbmRlbnRzIHVzZT8gCmBgYHtyfQpweV9yID0gZGF0YSAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoV29ya1Rvb2xzU2VsZWN0LCAnUicpKSAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoV29ya1Rvb2xzU2VsZWN0LCAnUHl0aG9uJykpICU+JSBtdXRhdGUocHIgPSAxKQojIGxlZnQgam9pbiAKcHlfciA9IHB5X3IgJT4lIHNlbGVjdChSZXNwb25kZW50LCBwcikKZGF0YTIgPSBsZWZ0X2pvaW4oZGF0YSxweV9yLCBieT0iUmVzcG9uZGVudCIpICU+JSBtdXRhdGUocHIgPSBpZl9lbHNlKGlzLm5hKHByKSwwLCBwcikpCkhtaXNjOjpkZXNjcmliZShhcy5mYWN0b3IoZGF0YTIkcHIpKQpgYGAKCiogMzY2MCBvdXQgb2YgODEzMiAoNDUlKSByZXNwb25kZW50cyBsaXN0ZWQgYm90aCBSIGFuZCBQeXRob24gaW4gd29yayB0b29scyB1c2VkLiAKCmBgYHtyfQp0b29sczIgPC0gZGF0YTIgICU+JSAKICAgIG11dGF0ZSh3b3JrX3Rvb2xzID1zdHJfc3BsaXQoV29ya1Rvb2xzU2VsZWN0LCIsIikgKSAgJT4lIAogICAgdW5uZXN0KHdvcmtfdG9vbHMpCgojIGdldCBwcm9wb3J0aW9uICAKdG9vbHMyICU+JSBmaWx0ZXIocHI9PTEpICU+JSBncm91cF9ieSh3b3JrX3Rvb2xzKSAlPiUgdGFsbHkoc29ydD1UKSAlPiUgZmlsdGVyKHdvcmtfdG9vbHMgIT0iUHl0aG9uIiAmIHdvcmtfdG9vbHMgIT0iUiIpICU+JSBtdXRhdGUocHJvcF9yZXMgPSByb3VuZChuLzM2NjAsMykpCmBgYAoKYGBge3J9CiMgcGxvdCBwcm9wb3J0aW9uIG9mIDEwIG1vc3QgZnJlcXVlbnQgdG9vbHMgdXNlZCBieSByZXNwb25kZW50cyB0aGF0IGxpc3RlZCBib3RoIFIgYW5kIFB5dGhvbiAod29yayB0b29scykKdG9vbHMyICU+JSBmaWx0ZXIocHI9PTEpICU+JSBncm91cF9ieSh3b3JrX3Rvb2xzKSAlPiUgdGFsbHkoc29ydD1UKSAlPiUgbXV0YXRlKHByb3BfcmVzID0gcm91bmQobi8zNjYwLDIpKSAlPiUgZmlsdGVyKHdvcmtfdG9vbHMgIT0iUHl0aG9uIiAmIHdvcmtfdG9vbHMgIT0iUiIpICU+JSBzbGljZSgxOjEwKSAlPiUgZ2dwbG90KGFlcyh4PXJlb3JkZXIod29ya190b29scyxwcm9wX3JlcyksIHk9cHJvcF9yZXMsIGZpbGw9d29ya190b29scykpICsgZ2VvbV9jb2wod2lkdGg9MC41KSArIGdlb21fdGV4dChzdGF0PSJpZGVudGl0eSIsYWVzKGxhYmVsPXByb3BfcmVzKSxoanVzdD0tMC41LHNpemU9Myxjb2xvcj0iYmxhY2siKSArIGNvb3JkX2ZsaXAoKSArIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeT0iUHJvcG9ydGlvbiIseD0iV29yayB0b29scyIpICsgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX2ZpbGxfc2ltcHNvbnMoKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygwLDEpKSAKYGBgCgoqIFNRTCwgSnVweXRlciBub3RlYm9va3MsIFRlbnNvcmZsb3csIFRhYmxlYXUgYW5kIEFtYXpvbiB3ZWIgc2VydmljZXMgYXJlIHRoZSBtb3N0IGZyZXF1ZW50IG90aGVyIHRvb2xzIGFtb25nc3QgdGhvc2UgcmVzcG9uZGVudHMgdGhhdCB1c2UgYm90aCBSIGFuZCBQeXRob24gKHRvb2xzKS4gCgoK