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

tidy_data = messy_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
Job Title Occurrences in the Dataset Percentage of the Dataset
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