PART 1: Basic Analysis
Load the Project’s Libraries
library(tidyverse)
library(plotly)
Get the Data
file_name = file.path(getwd(),"h1b_kaggle.csv.zip")
messy_data = read_csv(file_name, progress=FALSE)
dim(messy_data)
[1] 3002458 11
knitr::kable(head(messy_data) %>% select(-X1,-lon,-lat), digits=0)
Tidy the Data
Remove Defective Rows and Useless Columns
# Remove the index column
tidy_data = tidy_data %>% select(-X1)
# Discard rows with NA (overlook the lan and lon variables)
tidy_data = tidy_data %>% drop_na(-lon, -lat)
Convert Variables Types
tidy_data$CASE_STATUS = tidy_data %>% .$CASE_STATUS %>% as.factor()
tidy_data$EMPLOYER_NAME = tidy_data %>% .$EMPLOYER_NAME %>% as.factor()
tidy_data$SOC_NAME = tidy_data %>% .$SOC_NAME %>% as.factor()
tidy_data$JOB_TITLE = tidy_data %>% .$JOB_TITLE %>% as.factor()
tidy_data$FULL_TIME_POSITION = tidy_data %>% .$FULL_TIME_POSITION %>% as.factor()
tidy_data$WORKSITE = tidy_data %>% .$WORKSITE %>% as.factor()
Make Factor Levels Insensitive for Capitalization
e.g. Economists = economists are assigned as the same factor level
tidy_data = tidy_data %>%
map_if(is.factor, fct_relabel, fun=function(x) {x=str_to_title(x)}) %>%
as_tibble()
PART 2: Exploratory Data Analysis
Data Scientist Data Set Preprocessing
Filter Data Science Jobs
According to a recent survey in the data analytics domain, this is how practitioners call themselves:
- Data Scientist (31%)
- Data Analyst (12%)
- Researcher (11%)
- Business Analyst (9%)
- Statistician (6%)
- Predictive Modeler (5%)
- Other Job Titles (26%)
jobs_titles = c('Data Scientist',
'Data Analyst',
'Business Analyst',
'Statistician',
# Convert the job title vector into one regular expression query
jobs_pattern = jobs_titles %>% sapply(function(x) paste0("^",x,"*")) %>% paste(collapse='|')
tidy_jobs = tidy_data %>% filter(str_detect(JOB_TITLE,jobs_pattern))
# Drop unused facotor levels
tidy_jobs$JOB_TITLE = tidy_jobs %>% .[["JOB_TITLE"]] %>% fct_drop()
There are 1625 matching job titles and 52975 subsequent petitions
Collapse the different job title variations into their basic form of 5 titles:
Data Scientist, Data Analyst, Business Analyst, Statistician, Predictive Modeler
for(jobs_title in jobs_titles) {
# Find the rows which correspond the the current job title
rows_index = tidy_jobs %>% .$JOB_TITLE %>% str_detect(paste0("^",jobs_title,"*"))
# Replace the different title variations with their stem
tidy_jobs[rows_index,"JOB_TITLE"] = jobs_title
}
# Drop unused facotor levels
tidy_jobs$JOB_TITLE = tidy_jobs %>% .[["JOB_TITLE"]] %>% fct_drop()
There are 5 matching job titles and 52975 subsequent petitions
Business Analyst |
44471 |
84 |
Data Analyst |
5036 |
10 |
Data Scientist |
2234 |
4 |
Statistician |
1213 |
2 |
Predictive Modeler |
21 |
0 |
Remove Salary’s Outliers
Find the 2.5% and 97.5% quantiles and consider only records within these borders.
lims = tidy_jobs %>% .$PREVAILING_WAGE %>% quantile(c(2.5,97.5)/100)
tidy_jobs = tidy_jobs %>% filter(PREVAILING_WAGE > lims[1], PREVAILING_WAGE < lims[2])
Visualisations
For simplicity’s sake, a major part of this section the focus is solely on Data Science jobs.
Petitions Status of Data Science Jobs
ds_petitions_status <- ggplot(tidy_jobs, aes(fill=CASE_STATUS)) +
geom_bar(aes(x=JOB_TITLE), position="fill") +
coord_flip() +
theme(legend.position="bottom") +
# guides(fill=guide_legend(nrow=1))
labs(x="Job Title", y="Percentage", title='Petition Status of Data Scientists Jobs')
plot(ds_petitions_status)

Good News! 95 % of all Data Scientist petitions were certified.
Data Scientists’ Salary Distribution
Since the type of position (full-time vs. part-time) influences the salary, we condition the density plot on the position type.
salaryStats = tidy_jobs %>%
filter(JOB_TITLE=="Data Scientist") %>%
group_by(FULL_TIME_POSITION) %>%
summarise(med = median(PREVAILING_WAGE)/1e3)
ds_dens = ggplot(tidy_jobs %>% filter(JOB_TITLE=="Data Scientist")) +
geom_density(aes(x=PREVAILING_WAGE/1e3, color=FULL_TIME_POSITION), size=1.2) +
geom_vline(aes(xintercept=med, color=FULL_TIME_POSITION), salaryStats, size=1.2) +
theme_bw() + theme(legend.position="bottom") +
labs(x="Salary (in thousand USD)", y="Density of Data Science Salary",
title="Data Scientists' Salary Distribution by Position Type",
subtitle="With Salary Medians") +
scale_x_continuous(breaks=seq(40,150,5))
plot(ds_dens)

Data Scientists’ Salary Trend
trends_conditioned = tidy_jobs %>%
filter(JOB_TITLE=="Data Scientist") %>%
group_by(FULL_TIME_POSITION,YEAR) %>%
summarise(med = median(PREVAILING_WAGE)/1e3, N=n())
ds_trend <- ggplot(tidy_jobs %>% filter(JOB_TITLE=="Data Scientist"),
aes(x=YEAR, y=PREVAILING_WAGE/1e3, color=FULL_TIME_POSITION)) +
geom_point(aes(x=YEAR, y=med, color=FULL_TIME_POSITION, size=N), trends_conditioned) +
geom_line(aes(x=YEAR, y=med, color=FULL_TIME_POSITION), trends_conditioned) +
geom_smooth(method="lm", se=FALSE, linetype=9) +
scale_y_continuous(breaks=seq(40,140,5)) +
guides(size=guide_legend(title="Number of Jobs Petitions"),
color=guide_legend(title="Position Type")) +
labs(title="Data Scientists' Salary Trend",
subtitle="with the number of related petitions per year",
x="Year",y="Salary (in thousand USD)") +
theme_bw()
plot(ds_trend)

LS0tDQp0aXRsZTogIkgtMUIgVmlzYSBQZXRpdGlvbnMgMjAxMS0yMDE2Ig0Kc3VidGl0bGU6ICJUaGUgRGF0YSBTY2llbnRpc3QgQXNwZWN0Ig0KYXV0aG9yOiAiSGFyZWwgTHVzdGlnZXIiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgdGhlbWU6IGpvdXJuYWwNCiAgICB0b2M6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Kcm0obGlzdCA9IGxzKCkpOyBjYXQoIlwwMTQiKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTQuNSwgZmlnLmFsaWduPSJjZW50ZXIiKQ0KYGBgDQoNCi0tLQ0KDQojIFBBUlQgMTogQmFzaWMgQW5hbHlzaXMNCg0KIyMgTG9hZCB0aGUgUHJvamVjdCdzIExpYnJhcmllcw0KDQpgYGB7ciBsb2FkX2xpYnJhcmllcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShwbG90bHkpDQpgYGANCg0KIyMgR2V0IHRoZSBEYXRhDQoNCmBgYHtyIGdldF90aGVfZGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0NCmZpbGVfbmFtZSA9IGZpbGUucGF0aChnZXR3ZCgpLCJoMWJfa2FnZ2xlLmNzdi56aXAiKQ0KbWVzc3lfZGF0YSA9IHJlYWRfY3N2KGZpbGVfbmFtZSwgcHJvZ3Jlc3M9RkFMU0UpDQpkaW0obWVzc3lfZGF0YSkNCmtuaXRyOjprYWJsZShoZWFkKG1lc3N5X2RhdGEpICU+JSBzZWxlY3QoLVgxLC1sb24sLWxhdCksIGRpZ2l0cz0wKQ0KYGBgDQoNCiMjIFRpZHkgdGhlIERhdGENCg0KYGBge3IgdGlkeV9kYXRhfQ0KdGlkeV9kYXRhID0gbWVzc3lfZGF0YQ0KYGBgDQoNCiMjIyBSZW1vdmUgRGVmZWN0aXZlIFJvd3MgYW5kIFVzZWxlc3MgQ29sdW1ucw0KDQpgYGB7ciByZW1vdmVfY29sc19hbmRfcm93c30NCiMgUmVtb3ZlIHRoZSBpbmRleCBjb2x1bW4NCnRpZHlfZGF0YSA9IHRpZHlfZGF0YSAlPiUgc2VsZWN0KC1YMSkNCg0KIyBEaXNjYXJkIHJvd3Mgd2l0aCBOQSAob3Zlcmxvb2sgdGhlIGxhbiBhbmQgbG9uIHZhcmlhYmxlcykNCnRpZHlfZGF0YSA9IHRpZHlfZGF0YSAlPiUgZHJvcF9uYSgtbG9uLCAtbGF0KQ0KYGBgDQoNCiMjIyBDb252ZXJ0IFZhcmlhYmxlcyBUeXBlcw0KDQpgYGB7ciBjb252ZXJ0X3ZhcnNfdHlwZXN9DQp0aWR5X2RhdGEkQ0FTRV9TVEFUVVMgPSB0aWR5X2RhdGEgJT4lIC4kQ0FTRV9TVEFUVVMgJT4lIGFzLmZhY3RvcigpDQp0aWR5X2RhdGEkRU1QTE9ZRVJfTkFNRSA9IHRpZHlfZGF0YSAlPiUgLiRFTVBMT1lFUl9OQU1FICU+JSBhcy5mYWN0b3IoKQ0KdGlkeV9kYXRhJFNPQ19OQU1FID0gdGlkeV9kYXRhICU+JSAuJFNPQ19OQU1FICU+JSBhcy5mYWN0b3IoKQ0KdGlkeV9kYXRhJEpPQl9USVRMRSA9IHRpZHlfZGF0YSAlPiUgLiRKT0JfVElUTEUgJT4lIGFzLmZhY3RvcigpDQp0aWR5X2RhdGEkRlVMTF9USU1FX1BPU0lUSU9OID0gdGlkeV9kYXRhICU+JSAuJEZVTExfVElNRV9QT1NJVElPTiAlPiUgYXMuZmFjdG9yKCkNCnRpZHlfZGF0YSRXT1JLU0lURSA9IHRpZHlfZGF0YSAlPiUgLiRXT1JLU0lURSAlPiUgYXMuZmFjdG9yKCkNCmBgYA0KDQojIyMgTWFrZSBGYWN0b3IgTGV2ZWxzIEluc2Vuc2l0aXZlIGZvciBDYXBpdGFsaXphdGlvbg0KDQplLmcuICoqRWNvbm9taXN0cyA9IGVjb25vbWlzdHMqKiBhcmUgYXNzaWduZWQgYXMgdGhlIHNhbWUgZmFjdG9yIGxldmVsIA0KDQpgYGB7ciBtYWtlIGZhY3RvX2xldmVsc19pbnNlbnNpdGl2ZV9mb3JfY2FwaXRhbGl6YXRpb259DQp0aWR5X2RhdGEgPSB0aWR5X2RhdGEgJT4lIA0KICAgIG1hcF9pZihpcy5mYWN0b3IsIGZjdF9yZWxhYmVsLCBmdW49ZnVuY3Rpb24oeCkge3g9c3RyX3RvX3RpdGxlKHgpfSkgJT4lIA0KICAgIGFzX3RpYmJsZSgpDQpgYGANCg0KDQojIFBBUlQgMjogRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcw0KDQojIyBEYXRhIFNjaWVudGlzdCBEYXRhIFNldCBQcmVwcm9jZXNzaW5nDQoNCiMjIyBGaWx0ZXIgRGF0YSBTY2llbmNlIEpvYnMNCg0KQWNjb3JkaW5nIHRvIGEgcmVjZW50IHN1cnZleSBpbiB0aGUgZGF0YSBhbmFseXRpY3MgZG9tYWluLCB0aGlzIGlzIGhvdyANCnByYWN0aXRpb25lcnMgY2FsbCB0aGVtc2VsdmVzOg0KDQoqIERhdGEgU2NpZW50aXN0ICgzMSUpDQoqIERhdGEgQW5hbHlzdCAoMTIlKQ0KKiBSZXNlYXJjaGVyICgxMSUpDQoqIEJ1c2luZXNzIEFuYWx5c3QgKDklKQ0KKiBTdGF0aXN0aWNpYW4gKDYlKQ0KKiBQcmVkaWN0aXZlIE1vZGVsZXIgKDUlKQ0KKiBPdGhlciBKb2IgVGl0bGVzICgyNiUpDQoNCmBgYHtyIGZpbHRlcl9qb2JzfQ0Kam9ic190aXRsZXMgPSBjKCdEYXRhIFNjaWVudGlzdCcsDQogICAgICAgICAgICAgICAgJ0RhdGEgQW5hbHlzdCcsDQogICAgICAgICAgICAgICAgJ0J1c2luZXNzIEFuYWx5c3QnLA0KICAgICAgICAgICAgICAgICdTdGF0aXN0aWNpYW4nLA0KICAgICAgICAgICAgICAgICdQcmVkaWN0aXZlIE1vZGVsZXInKQ0KIyBDb252ZXJ0IHRoZSBqb2IgdGl0bGUgdmVjdG9yIGludG8gb25lIHJlZ3VsYXIgZXhwcmVzc2lvbiBxdWVyeQ0Kam9ic19wYXR0ZXJuID0gam9ic190aXRsZXMgJT4lIHNhcHBseShmdW5jdGlvbih4KSBwYXN0ZTAoIl4iLHgsIioiKSkgJT4lIHBhc3RlKGNvbGxhcHNlPSd8JykNCnRpZHlfam9icyA9IHRpZHlfZGF0YSAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoSk9CX1RJVExFLGpvYnNfcGF0dGVybikpIA0KIyBEcm9wIHVudXNlZCBmYWNvdG9yIGxldmVscw0KdGlkeV9qb2JzJEpPQl9USVRMRSA9IHRpZHlfam9icyAlPiUgLltbIkpPQl9USVRMRSJdXSAlPiUgZmN0X2Ryb3AoKQ0KYGBgDQoNCmBgYHtyIGZpbHRlcl9qb2JzX3N0YXRpc3RpY3NfMSwgZWNobz1GQUxTRX0NCm5fbGV2ZWxzID0gdGlkeV9qb2JzICU+JSAuW1siSk9CX1RJVExFIl1dICU+JSBubGV2ZWxzDQpuX3Jvd3MgPSBucm93KHRpZHlfam9icykNCmNhdCgiVGhlcmUgYXJlIixuX2xldmVscywibWF0Y2hpbmcgam9iIHRpdGxlcyBhbmQiLG5fcm93cywic3Vic2VxdWVudCBwZXRpdGlvbnMiKQ0KYGBgDQoNCkNvbGxhcHNlIHRoZSBkaWZmZXJlbnQgam9iIHRpdGxlIHZhcmlhdGlvbnMgaW50byB0aGVpciBiYXNpYyBmb3JtIG9mDQpgciBsZW5ndGgoam9ic190aXRsZXMpYCB0aXRsZXM6ICAgDQpgciBqb2JzX3RpdGxlc2ANCg0KYGBge3IgY29sbGFwc2VfZmFjdG9yX2xldmVsc19pbnRvX3RoZW1lX2dyb3Vwc30NCmZvcihqb2JzX3RpdGxlIGluIGpvYnNfdGl0bGVzKSB7DQogICMgRmluZCB0aGUgcm93cyB3aGljaCBjb3JyZXNwb25kIHRoZSB0aGUgY3VycmVudCBqb2IgdGl0bGUgDQogIHJvd3NfaW5kZXggPSB0aWR5X2pvYnMgJT4lIC4kSk9CX1RJVExFICU+JSBzdHJfZGV0ZWN0KHBhc3RlMCgiXiIsam9ic190aXRsZSwiKiIpKQ0KICAjIFJlcGxhY2UgdGhlIGRpZmZlcmVudCB0aXRsZSB2YXJpYXRpb25zIHdpdGggdGhlaXIgc3RlbQ0KICB0aWR5X2pvYnNbcm93c19pbmRleCwiSk9CX1RJVExFIl0gPSBqb2JzX3RpdGxlDQp9IA0KIyBEcm9wIHVudXNlZCBmYWNvdG9yIGxldmVscw0KdGlkeV9qb2JzJEpPQl9USVRMRSA9IHRpZHlfam9icyAlPiUgLltbIkpPQl9USVRMRSJdXSAlPiUgZmN0X2Ryb3AoKQ0KYGBgDQoNCmBgYHtyIGZpbHRlcl9qb2JzX3N0YXRpc3RpY3NfMiwgZWNobz1GQUxTRSwgcmVzdWx0cz0iYXMuaXMifQ0Kbl9sZXZlbHMgPSB0aWR5X2pvYnMgJT4lIC5bWyJKT0JfVElUTEUiXV0gJT4lIG5sZXZlbHMNCm5fcm93cyA9IG5yb3codGlkeV9qb2JzKQ0KY2F0KCJUaGVyZSBhcmUiLG5fbGV2ZWxzLCJtYXRjaGluZyBqb2IgdGl0bGVzIGFuZCIsbl9yb3dzLCJzdWJzZXF1ZW50IHBldGl0aW9ucyIpDQoNCmtuaXRyOjprYWJsZSgNCiAgICB0aWR5X2pvYnMgJT4lIGdyb3VwX2J5KEpPQl9USVRMRSkgJT4lIHN1bW1hcmlzZShOPW4oKSwgUGVyY2VudD1yb3VuZCgxMDAqTi9ucm93KHRpZHlfam9icykpKSAlPiUgYXJyYW5nZShkZXNjKE4pKSwNCiAgICBjb2wubmFtZXM9YygiSm9iIFRpdGxlIiwiT2NjdXJyZW5jZXMgaW4gdGhlIERhdGFzZXQiLCJQZXJjZW50YWdlIG9mIHRoZSBEYXRhc2V0IiksDQogICAgZm9ybWF0PSJtYXJrZG93biIsIGNhcHRpb249IkpvYiBUaXRsZSBTdW1tYXJ5IFN0YXRpc3RpY3MiKQ0KYGBgDQoNCiMjIyBSZW1vdmUgU2FsYXJ5J3MgT3V0bGllcnMNCg0KRmluZCB0aGUgMi41JSBhbmQgOTcuNSUgcXVhbnRpbGVzIGFuZCBjb25zaWRlciBvbmx5IHJlY29yZHMgd2l0aGluIHRoZXNlIA0KYm9yZGVycy4NCg0KYGBge3IgcmVtb3ZlX3NhbGFyeV9vdXRsaWVyc30NCmxpbXMgPSB0aWR5X2pvYnMgJT4lIC4kUFJFVkFJTElOR19XQUdFICU+JSBxdWFudGlsZShjKDIuNSw5Ny41KS8xMDApDQp0aWR5X2pvYnMgPSB0aWR5X2pvYnMgJT4lIGZpbHRlcihQUkVWQUlMSU5HX1dBR0UgPiBsaW1zWzFdLCBQUkVWQUlMSU5HX1dBR0UgPCBsaW1zWzJdKQ0KYGBgDQoNCg0KIyMgVmlzdWFsaXNhdGlvbnMNCg0KRm9yIHNpbXBsaWNpdHkncyBzYWtlLCBhIG1ham9yIHBhcnQgb2YgdGhpcyBzZWN0aW9uIHRoZSBmb2N1cyBpcyBzb2xlbHkgb24gDQoqKkRhdGEgU2NpZW5jZSoqIGpvYnMuDQoNCiMjIyBQZXRpdGlvbnMgU3RhdHVzIG9mIERhdGEgU2NpZW5jZSBKb2JzDQoNCmBgYHtyIGRhdGFfc2NpZW50aXN0c19wZXRpdGlvbnNfc3RhdHVzLCBmaWcuYXNwPS41fQ0KZHNfcGV0aXRpb25zX3N0YXR1cyA8LSBnZ3Bsb3QodGlkeV9qb2JzLCBhZXMoZmlsbD1DQVNFX1NUQVRVUykpICsNCiAgICBnZW9tX2JhcihhZXMoeD1KT0JfVElUTEUpLCBwb3NpdGlvbj0iZmlsbCIpICsNCiAgICBjb29yZF9mbGlwKCkgKyANCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsNCiAgICAjIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTEpKQ0KICAgIGxhYnMoeD0iSm9iIFRpdGxlIiwgeT0iUGVyY2VudGFnZSIsIHRpdGxlPSdQZXRpdGlvbiBTdGF0dXMgb2YgRGF0YSBTY2llbnRpc3RzIEpvYnMnKQ0KICANCnBsb3QoZHNfcGV0aXRpb25zX3N0YXR1cykNCmBgYA0KDQpgYGB7ciBkYXRhX3NjaWVudGlzdHNfcGV0aXRpb25zX3N0YXRzLCBlY2hvPUZBTFNFfQ0KcGV0X3N0YXRzID0gdGlkeV9qb2JzICU+JSBmaWx0ZXIoSk9CX1RJVExFPT0iRGF0YSBTY2llbnRpc3QiKSAlPiUgDQogICAgZ3JvdXBfYnkoQ0FTRV9TVEFUVVMpICU+JSBzdW1tYXJpemUoU1RBVFVTID0gbigpKQ0KY2VydGlmaWVkX3BlciA9IHN1bShwZXRfc3RhdHMgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoQ0FTRV9TVEFUVVMgJWluJSBjKCJDZXJ0aWZpZWQiLCJDZXJ0aWZpZWQtV2l0aGRyYXduIikpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFNUQVRVUykpIC8gc3VtKHBldF9zdGF0cyAlPiUgc2VsZWN0KFNUQVRVUykpDQpgYGANCg0KR29vZCBOZXdzISBgciByb3VuZCgxMDAqY2VydGlmaWVkX3BlcilgICUgb2YgYWxsIERhdGEgU2NpZW50aXN0IHBldGl0aW9ucyB3ZXJlIA0KY2VydGlmaWVkLg0KDQo8IS0tDQpXaGF0IGlzIHRoZSB0b3RhbCAlIG9mIGNlcnRpZmllZCArIGNlcnRpZmllZC13aXRoZHJhd24gaW4gdGhpcyB2b2NhdGlvbg0KLS0+DQoNCiMjIyBEYXRhIFNjaWVudGlzdHMnIFNhbGFyeSBEaXN0cmlidXRpb24NCg0KU2luY2UgdGhlIHR5cGUgb2YgcG9zaXRpb24gKGZ1bGwtdGltZSB2cy4gcGFydC10aW1lKSBpbmZsdWVuY2VzIHRoZSBzYWxhcnksIHdlIA0KY29uZGl0aW9uIHRoZSBkZW5zaXR5IHBsb3Qgb24gdGhlIHBvc2l0aW9uIHR5cGUuIA0KDQpgYGB7ciBkYXRhX3NjaWVudGlzdHNfc2FsYXJ5X2Rpc3RyaWJ1dGlvbn0NCnNhbGFyeVN0YXRzID0gdGlkeV9qb2JzICU+JSANCiAgICBmaWx0ZXIoSk9CX1RJVExFPT0iRGF0YSBTY2llbnRpc3QiKSAlPiUgDQogICAgZ3JvdXBfYnkoRlVMTF9USU1FX1BPU0lUSU9OKSAlPiUNCiAgICBzdW1tYXJpc2UobWVkID0gbWVkaWFuKFBSRVZBSUxJTkdfV0FHRSkvMWUzKQ0KICANCmRzX2RlbnMgPSBnZ3Bsb3QodGlkeV9qb2JzICU+JSBmaWx0ZXIoSk9CX1RJVExFPT0iRGF0YSBTY2llbnRpc3QiKSkgKw0KICAgIGdlb21fZGVuc2l0eShhZXMoeD1QUkVWQUlMSU5HX1dBR0UvMWUzLCBjb2xvcj1GVUxMX1RJTUVfUE9TSVRJT04pLCBzaXplPTEuMikgKyANCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lZCwgY29sb3I9RlVMTF9USU1FX1BPU0lUSU9OKSwgc2FsYXJ5U3RhdHMsIHNpemU9MS4yKSArIA0KICAgIHRoZW1lX2J3KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsNCiAgICBsYWJzKHg9IlNhbGFyeSAoaW4gdGhvdXNhbmQgVVNEKSIsIHk9IkRlbnNpdHkgb2YgRGF0YSBTY2llbmNlIFNhbGFyeSIsDQogICAgICAgICB0aXRsZT0iRGF0YSBTY2llbnRpc3RzJyBTYWxhcnkgRGlzdHJpYnV0aW9uIGJ5IFBvc2l0aW9uIFR5cGUiLA0KICAgICAgICAgc3VidGl0bGU9IldpdGggU2FsYXJ5IE1lZGlhbnMiKSArIA0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDQwLDE1MCw1KSkNCiAgDQpwbG90KGRzX2RlbnMpDQpgYGANCg0KPCEtLSAjIyMgRGF0YSBTY2llbnRpc3RzJyBKb2JzIFBldGl0aW9ucyAtLT4NCg0KYGBge3Igam9iX3BldGl0aW9ucywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnNhbGFyeVN0YXRzID0gdGlkeV9qb2JzICU+JSANCiAgICBmaWx0ZXIoSk9CX1RJVExFPT0iRGF0YSBTY2llbnRpc3QiKSAlPiUgDQogICAgZ3JvdXBfYnkoRlVMTF9USU1FX1BPU0lUSU9OLFlFQVIpICU+JQ0KICAgIHN1bW1hcmlzZShOPW4oKSkNCg0KZHNfcGV0aXRpb25zIDwtIGdncGxvdCgpICsNCiAgICBnZW9tX2xpbmUoYWVzKHg9WUVBUiwgeT1OLCBjb2xvcj1GVUxMX1RJTUVfUE9TSVRJT04pLCBzYWxhcnlTdGF0cykNCg0KcGxvdChkc19wZXRpdGlvbnMpDQpgYGANCg0KIyMjIERhdGEgU2NpZW50aXN0cycgU2FsYXJ5IFRyZW5kDQoNCmBgYHtyIHNhbGFyeV90cmVuZHN9DQp0cmVuZHNfY29uZGl0aW9uZWQgPSB0aWR5X2pvYnMgJT4lIA0KICAgIGZpbHRlcihKT0JfVElUTEU9PSJEYXRhIFNjaWVudGlzdCIpICU+JSANCiAgICBncm91cF9ieShGVUxMX1RJTUVfUE9TSVRJT04sWUVBUikgJT4lDQogICAgc3VtbWFyaXNlKG1lZCA9IG1lZGlhbihQUkVWQUlMSU5HX1dBR0UpLzFlMywgTj1uKCkpIA0KICANCmRzX3RyZW5kIDwtIGdncGxvdCh0aWR5X2pvYnMgJT4lIGZpbHRlcihKT0JfVElUTEU9PSJEYXRhIFNjaWVudGlzdCIpLA0KICAgICAgICAgICAgICAgICAgIGFlcyh4PVlFQVIsIHk9UFJFVkFJTElOR19XQUdFLzFlMywgY29sb3I9RlVMTF9USU1FX1BPU0lUSU9OKSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKHg9WUVBUiwgeT1tZWQsIGNvbG9yPUZVTExfVElNRV9QT1NJVElPTiwgc2l6ZT1OKSwgdHJlbmRzX2NvbmRpdGlvbmVkKSArDQogICAgZ2VvbV9saW5lKGFlcyh4PVlFQVIsIHk9bWVkLCBjb2xvcj1GVUxMX1RJTUVfUE9TSVRJT04pLCB0cmVuZHNfY29uZGl0aW9uZWQpICsNCiAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UsIGxpbmV0eXBlPTkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg0MCwxNDAsNSkpICsNCiAgICBndWlkZXMoc2l6ZT1ndWlkZV9sZWdlbmQodGl0bGU9Ik51bWJlciBvZiBKb2JzIFBldGl0aW9ucyIpLA0KICAgICAgICAgICBjb2xvcj1ndWlkZV9sZWdlbmQodGl0bGU9IlBvc2l0aW9uIFR5cGUiKSkgKw0KICAgIGxhYnModGl0bGU9IkRhdGEgU2NpZW50aXN0cycgU2FsYXJ5IFRyZW5kIiwgDQogICAgICAgICBzdWJ0aXRsZT0id2l0aCB0aGUgbnVtYmVyIG9mIHJlbGF0ZWQgcGV0aXRpb25zIHBlciB5ZWFyIiwNCiAgICAgICAgIHg9IlllYXIiLHk9IlNhbGFyeSAoaW4gdGhvdXNhbmQgVVNEKSIpICsNCiAgICB0aGVtZV9idygpDQogICANCnBsb3QoZHNfdHJlbmQpDQpgYGANCg0K