Code
Get Me BIG DATA, Stat!
Introduction
In the age of information, as you might know the amount of data available is expansive and growing exponentially. This leads to the need of data services like Microsoft Azure, Amazon AWS, and Google Cloud. Services that let any business store and run analysis on the data to see what works best for their company by tracking views, purchases, and even to test advertising. I personally have not explored any of these products until the start of this project. I wanted to get a more aqquainted with these services and choose Google’s Cloud Services to start with because they had some datasets that were already available to do projects on in Google’s Big Query. This is great because I don’t have insanely large data sets just laying around to test on! Yet. If you want to follow along and dip your toes into a major cloud service I will go step-by-step below. To get started, you will need to setup a free account with Google’s Big Query.
Click [HERE ] to start an account with Google Cloud and Big Query!
Goal of Project
The goal of this project is to demonstrate how anyone can utilize a cloud service and analyze the information from it. I will take a particular dataset already on the cloud and run a simple query on it to reduce the dimensions of the dataset to something simple. I will then visualize the data, run some machine learning models against the data for predictive analytics analysis on the data with an added visual for easy interpretation.
Data
The data I will use for the project is the Census_adult_income, which is located in the ml_datasets.
Data Definitions
From the project we are only going to utilize three variables to make things simple. The three variables used are income_bracket, sex, and education_num.
income_bracket : Either “>50K” or “<=50K” based on income.
sex : Gender of the observation.
education_num : Estimated years of education completed based on the value of the education field.
Data Query Process
Big Query supports two dialects of the coding language SQL, both standard and legacy. This is the query used to select the variables we will be working with. After running the query you can download the data to run analysis on.
Data Table
Below is a sample view of the data that was imported.
Now that we have data, we need to analyze it. To do this we need to make sure it is complete and of the proper type. Just looking at the table we see that two of the variables are of the character type. Since these columns only have two responses, the data will work better as binary.
Check data for missing observations.
There isn’t any missing observations in the entire data frame. Next step will be to alter the data types so we can work with it a little easier. This can be done by converting the categorical variables from character into binary format to make the observations numeric.
# if-else statement to make observations more than 50K equal to 1, less than 50K to 0, and convert to integer.
df$income_bracket_bin <- as.integer(ifelse(df$income_bracket == " >50K", "1", "0"))
# if-else statement to make observations male equal to 1, female to 0, and convert to integer.
df$sex_bin <- as.integer(ifelse(df$sex == " Male", "1", "0"))
The data now seems to be in order. Now, to visualize the data.
Visual Representation of Data
Violin Plot
Stacked Barplot
Analysis
For fun, we can try to predict the income bracket people will fall into based on sex and education using a few different machine learning models. To do so, we will need to split the data into training and testing. I organized the visuals for the different machine learning models in decreasing order by accuracy.
Machine learning Visuals
Neural Network Plot
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 4465 884
## 1 479 684
##
## Accuracy : 0.7907
## 95% CI : (0.7806, 0.8005)
## No Information Rate : 0.7592
## P-Value [Acc > NIR] : 8.787e-10
##
## Kappa : 0.3722
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Precision : 0.5881
## Recall : 0.4362
## F1 : 0.5009
## Prevalence : 0.2408
## Detection Rate : 0.1050
## Detection Prevalence : 0.1786
## Balanced Accuracy : 0.6697
##
## 'Positive' Class : 1
##
Decision Tree
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 4471 894
## 1 473 674
##
## Accuracy : 0.7901
## 95% CI : (0.78, 0.7999)
## No Information Rate : 0.7592
## P-Value [Acc > NIR] : 1.828e-09
##
## Kappa : 0.3679
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Sensitivity : 0.9043
## Specificity : 0.4298
## Pos Pred Value : 0.8334
## Neg Pred Value : 0.5876
## Prevalence : 0.7592
## Detection Rate : 0.6866
## Detection Prevalence : 0.8239
## Balanced Accuracy : 0.6671
##
## 'Positive' Class : 0
##
Naive Bayes Plot
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 4908 1438
## 1 36 130
##
## Accuracy : 0.7736
## 95% CI : (0.7633, 0.7838)
## No Information Rate : 0.7592
## P-Value [Acc > NIR] : 0.003202
##
## Kappa : 0.1089
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Precision : 0.78313
## Recall : 0.08291
## F1 : 0.14994
## Prevalence : 0.24079
## Detection Rate : 0.01996
## Detection Prevalence : 0.02549
## Balanced Accuracy : 0.53781
##
## 'Positive' Class : 1
##
We can somewhat accurately predict income bracket based on gender and education level. This means that there is some correlation between the variables. Normally you could just use the pairs function to view the correlation between the variables, but since the variables are mostly binary it will not have much readability. The question I am most curious about with this data, that has not been answered yet, would be the proportions of the two variables that are in a income bracket above fifty-thousand. This may be best observed with the use of tables.
Proportion Tables
Gender of Income Above 50K
## [1] "Counts"
##
## Female Male
## FALSE 9592 15128
## TRUE 1179 6662
## [1] "Percentages"
##
## Female Male
## FALSE 29.458555 46.460490
## TRUE 3.620896 20.460060
Conclusion
Nearly half of the observations are male above 50K income level. There is some bias here as the proportion of men to women has a ratio of almost 2:1. The ratios of sex in the higher income differ quite a bit. For female, it is 1:10 of >50K to <=50K. For Male, it is 1:2. This means that for the genders, only a 10th of the female population make a great wage while nearly a third of the male population is above 50K of income.
Education Level of Income Above 50K
## [1] "Counts"
##
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14
## FALSE 51 162 317 606 487 871 1115 400 8826 5904 1021 802 3134 764
## TRUE 0 6 16 40 27 62 60 33 1675 1387 361 265 2221 959
##
## 15 16
## FALSE 153 107
## TRUE 423 306
## [1] "Percentages"
##
## 1 2 3 4 5 6
## FALSE 0.15662910 0.49752772 0.97355732 1.86112220 1.49565431 2.67497927
## TRUE 0.00000000 0.01842695 0.04913854 0.12284635 0.08292129 0.19041184
##
## 7 8 9 10 11 12
## FALSE 3.42434200 1.22846350 27.10604711 18.13212125 3.13565308 2.46306932
## TRUE 0.18426952 0.10134824 5.14419090 4.25969718 1.10868831 0.81385707
##
## 13 14 15 16
## FALSE 9.62501152 2.34636528 0.46988729 0.32861399
## TRUE 6.82104358 2.94524124 1.29910015 0.93977458
Conclusion
Most of the population has a education level between 9 and 14. For the most part, the ratio for the same education numbers are 1:4, the 1 being those in the income bracket above 50K.
Education Level of Income Above 50K and Male
## [1] "Counts"
##
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14
## FALSE 51 162 319 607 492 874 1123 404 9052 6101 1088 858 3473 943
## TRUE 0 6 14 39 22 59 52 29 1449 1190 294 209 1882 780
##
## 15 16
## FALSE 195 157
## TRUE 381 256
## [1] "Percentages"
##
## 1 2 3 4 5 6
## FALSE 0.15662910 0.49752772 0.97969964 1.86419336 1.51101010 2.68419275
## TRUE 0.00000000 0.01842695 0.04299622 0.11977519 0.06756549 0.18119837
##
## 7 8 9 10 11 12
## FALSE 3.44891127 1.24074813 27.80012899 18.73713952 3.34142072 2.63505421
## TRUE 0.15970025 0.08906360 4.45010903 3.65467891 0.90292067 0.64187218
##
## 13 14 15 16
## FALSE 10.66613433 2.89610270 0.59887596 0.48217192
## TRUE 5.77992076 2.39550382 1.17011148 0.78621664
Conclusion
For the male population above 50K, the majority fall on education numbers of 9 and 13.
Education Level of Income Above 50K and Female
## [1] "Counts"
##
## 1 2 3 4 5 6 7 8 9 10 11 12
## FALSE 51 168 331 645 509 930 1167 429 10275 7094 1315 1011
## TRUE 0 0 2 1 5 3 8 4 226 197 67 56
##
## 13 14 15 16
## FALSE 5016 1544 534 363
## TRUE 339 179 42 50
## [1] "Percentages"
##
## 1 2 3 4 5
## FALSE 0.156629096 0.515954670 1.016553546 1.980897393 1.563219803
## TRUE 0.000000000 0.000000000 0.006142317 0.003071159 0.015355794
##
## 6 7 8 9 10
## FALSE 2.856177636 3.584042259 1.317527103 31.556156138 21.786800160
## TRUE 0.009213476 0.024569270 0.012284635 0.694081877 0.605018273
##
## 11 12 13 14 15
## FALSE 4.038573754 3.104941494 15.404932281 4.741869107 1.639998772
## TRUE 0.205767636 0.171984890 1.041122816 0.549737416 0.128988667
##
## 16
## FALSE 1.114830626
## TRUE 0.153557937
Conclusion
For the female population above 50K, the majority fall on education numbers of 9 and 13 similar to the males. The percentages are much smaller, but that was expected from the earlier table conclusions.
Closing
I hope you enjoyed this write-up and learned something from it. If you wish to see some of my other projects you can check out my portfolio website, github, or Linkedin.
[Portfolio ]
[Github ]
[Linkedin ]
Session Info
sessionInfo()
## R version 4.1.2 (2021-11-01)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19042)
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=English_United States.1252
## [2] LC_CTYPE=English_United States.1252
## [3] LC_MONETARY=English_United States.1252
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.1252
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] naivebayes_0.9.7 rpart.plot_3.1.0 rpart_4.1-15 dplyr_1.0.7
## [5] caret_6.0-90 lattice_0.20-45 neuralnet_1.44.2 viridis_0.6.2
## [9] viridisLite_0.4.0 ggplot2_3.3.5 Amelia_1.8.0 Rcpp_1.0.7
##
## loaded via a namespace (and not attached):
## [1] sass_0.4.0 jsonlite_1.7.2 splines_4.1.2
## [4] foreach_1.5.1 prodlim_2019.11.13 bslib_0.3.1
## [7] assertthat_0.2.1 stats4_4.1.2 highr_0.9
## [10] yaml_2.2.1 globals_0.14.0 ipred_0.9-12
## [13] pillar_1.6.4 glue_1.5.1 pROC_1.18.0
## [16] digest_0.6.28 colorspace_2.0-2 recipes_0.1.17
## [19] htmltools_0.5.2 Matrix_1.3-4 plyr_1.8.6
## [22] timeDate_3043.102 pkgconfig_2.0.3 listenv_0.8.0
## [25] bookdown_0.24 purrr_0.3.4 scales_1.1.1
## [28] gower_0.2.2 lava_1.6.10 proxy_0.4-26
## [31] tibble_3.1.6 generics_0.1.1 farver_2.1.0
## [34] ellipsis_0.3.2 withr_2.4.2 nnet_7.3-16
## [37] survival_3.2-13 magrittr_2.0.1 crayon_1.4.2
## [40] evaluate_0.14 fansi_0.5.0 future_1.23.0
## [43] parallelly_1.29.0 nlme_3.1-153 MASS_7.3-54
## [46] foreign_0.8-81 class_7.3-19 data.table_1.14.2
## [49] tools_4.1.2 lifecycle_1.0.1 stringr_1.4.0
## [52] munsell_0.5.0 e1071_1.7-9 compiler_4.1.2
## [55] jquerylib_0.1.4 rlang_0.4.12 grid_4.1.2
## [58] iterators_1.0.13 labeling_0.4.2 rmarkdown_2.11
## [61] ModelMetrics_1.2.2.2 gtable_0.3.0 codetools_0.2-18
## [64] DBI_1.1.1 reshape2_1.4.4 R6_2.5.1
## [67] gridExtra_2.3 lubridate_1.8.0 knitr_1.36
## [70] fastmap_1.1.0 future.apply_1.8.1 utf8_1.2.2
## [73] stringi_1.7.5 parallel_4.1.2 rmdformats_1.0.3
## [76] vctrs_0.3.8 tidyselect_1.1.1 xfun_0.28
LS0tDQp0aXRsZTogIkdldCBNZSBCSUcgREFUQSwgU3RhdCEiDQpvdXRwdXQ6DQogIHJtZGZvcm1hdHM6OmRvd25jdXRlOg0KICAgIGRvd25jdXRlX3RoZW1lOiAiY2hhb3MiDQogICAgbGlnaHRib3g6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMg0KLS0tDQoNCjxicj4NCg0KIVtHT09HTEUgSU1BR0VTXShodHRwczovL3dhbGxwYXBlcmFjY2Vzcy5jb20vZnVsbC8xODA1NDY5LmpwZykNCg0KIyBfX0ludHJvZHVjdGlvbl9fDQoNCnwgICAgSW4gdGhlIGFnZSBvZiBpbmZvcm1hdGlvbiwgYXMgeW91IG1pZ2h0IGtub3cgdGhlIGFtb3VudCBvZiBkYXRhIGF2YWlsYWJsZSBpcyBleHBhbnNpdmUgYW5kIGdyb3dpbmcgZXhwb25lbnRpYWxseS4gVGhpcyBsZWFkcyB0byB0aGUgbmVlZCBvZiBkYXRhIHNlcnZpY2VzIGxpa2UgTWljcm9zb2Z0IEF6dXJlLCBBbWF6b24gQVdTLCBhbmQgR29vZ2xlIENsb3VkLiBTZXJ2aWNlcyB0aGF0IGxldCBhbnkgYnVzaW5lc3Mgc3RvcmUgYW5kIHJ1biBhbmFseXNpcyBvbiB0aGUgZGF0YSB0byBzZWUgd2hhdCB3b3JrcyBiZXN0IGZvciB0aGVpciBjb21wYW55IGJ5IHRyYWNraW5nIHZpZXdzLCBwdXJjaGFzZXMsIGFuZCBldmVuIHRvIHRlc3QgYWR2ZXJ0aXNpbmcuIEkgcGVyc29uYWxseSBoYXZlIG5vdCBleHBsb3JlZCBhbnkgb2YgdGhlc2UgcHJvZHVjdHMgdW50aWwgdGhlIHN0YXJ0IG9mIHRoaXMgcHJvamVjdC4gSSB3YW50ZWQgdG8gZ2V0IGEgbW9yZSBhcXF1YWludGVkIHdpdGggdGhlc2Ugc2VydmljZXMgYW5kIGNob29zZSBHb29nbGUncyBDbG91ZCBTZXJ2aWNlcyB0byBzdGFydCB3aXRoIGJlY2F1c2UgdGhleSBoYWQgc29tZSBkYXRhc2V0cyB0aGF0IHdlcmUgYWxyZWFkeSBhdmFpbGFibGUgdG8gZG8gcHJvamVjdHMgb24gaW4gR29vZ2xlJ3MgQmlnIFF1ZXJ5LiBUaGlzIGlzIGdyZWF0IGJlY2F1c2UgSSBkb24ndCBoYXZlIGluc2FuZWx5IGxhcmdlIGRhdGEgc2V0cyBqdXN0IGxheWluZyBhcm91bmQgdG8gdGVzdCBvbiEgWWV0LiBJZiB5b3Ugd2FudCB0byBmb2xsb3cgYWxvbmcgYW5kIGRpcCB5b3VyIHRvZXMgaW50byBhIG1ham9yIGNsb3VkIHNlcnZpY2UgSSB3aWxsIGdvIHN0ZXAtYnktc3RlcCBiZWxvdy4gVG8gZ2V0IHN0YXJ0ZWQsIHlvdSB3aWxsIG5lZWQgdG8gc2V0dXAgYSBmcmVlIGFjY291bnQgd2l0aCBHb29nbGUncyBCaWcgUXVlcnkuIA0KDQo8YnI+DQoNCl9fQ2xpY2sgIVtbSEVSRV0oaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2JpZ3F1ZXJ5LyldIHRvIHN0YXJ0IGFuIGFjY291bnQgd2l0aCBHb29nbGUgQ2xvdWQgYW5kIEJpZyBRdWVyeSFfXw0KDQojIF9fR29hbCBvZiBQcm9qZWN0X18NCg0KfCAgICBUaGUgZ29hbCBvZiB0aGlzIHByb2plY3QgaXMgdG8gZGVtb25zdHJhdGUgaG93IGFueW9uZSBjYW4gdXRpbGl6ZSBhIGNsb3VkIHNlcnZpY2UgYW5kIGFuYWx5emUgdGhlIGluZm9ybWF0aW9uIGZyb20gaXQuIEkgd2lsbCB0YWtlIGEgcGFydGljdWxhciBkYXRhc2V0IGFscmVhZHkgb24gdGhlIGNsb3VkIGFuZCBydW4gYSBzaW1wbGUgcXVlcnkgb24gaXQgdG8gcmVkdWNlIHRoZSBkaW1lbnNpb25zIG9mIHRoZSBkYXRhc2V0IHRvIHNvbWV0aGluZyBzaW1wbGUuIEkgd2lsbCB0aGVuIHZpc3VhbGl6ZSB0aGUgZGF0YSwgcnVuIHNvbWUgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgYWdhaW5zdCB0aGUgZGF0YSBmb3IgcHJlZGljdGl2ZSBhbmFseXRpY3MgYW5hbHlzaXMgb24gdGhlIGRhdGEgd2l0aCBhbiBhZGRlZCB2aXN1YWwgZm9yIGVhc3kgaW50ZXJwcmV0YXRpb24uIA0KDQoNCg0KIyBfX0RhdGFfXw0KDQpUaGUgZGF0YSBJIHdpbGwgdXNlIGZvciB0aGUgcHJvamVjdCBpcyB0aGUgQ2Vuc3VzX2FkdWx0X2luY29tZSwgd2hpY2ggaXMgbG9jYXRlZCBpbiB0aGUgbWxfZGF0YXNldHMuDQoNCjxicj4NCg0KIVtdKEM6L1VzZXJzL0NTRmljL0Rlc2t0b3AvRGF0YSBJbmZvLlBORykNCg0KDQojIyBfX0RhdGEgRGVmaW5pdGlvbnNfXw0KDQpGcm9tIHRoZSBwcm9qZWN0IHdlIGFyZSBvbmx5IGdvaW5nIHRvIHV0aWxpemUgdGhyZWUgdmFyaWFibGVzIHRvIG1ha2UgdGhpbmdzIHNpbXBsZS4gVGhlIHRocmVlIHZhcmlhYmxlcyB1c2VkIGFyZSBpbmNvbWVfYnJhY2tldCwgc2V4LCBhbmQgZWR1Y2F0aW9uX251bS4NCg0KPGJyPg0KDQoqIF9faW5jb21lX2JyYWNrZXRfXzogRWl0aGVyICI+NTBLIiBvciAiPD01MEsiIGJhc2VkIG9uIGluY29tZS4NCiogX19zZXhfXzogR2VuZGVyIG9mIHRoZSBvYnNlcnZhdGlvbi4NCiogX19lZHVjYXRpb25fbnVtX186IEVzdGltYXRlZCB5ZWFycyBvZiBlZHVjYXRpb24gY29tcGxldGVkIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiB0aGUgZWR1Y2F0aW9uIGZpZWxkLg0KDQo8YnI+DQoNCiFbTGlzdCBvZiBhbGwgb2YgdGhlIGRhdGEgdmFyaWFibGVzLCB0eXBlcywgYW5kIGRlZmluaXRpb25zIG9mIHRoZSBzZXQuXShDOi9Vc2Vycy9DU0ZpYy9EZXNrdG9wL1NjaGVtYS5QTkcpDQoNCg0KDQoNCiMjIF9fRGF0YSBRdWVyeSBQcm9jZXNzX18NCg0KQmlnIFF1ZXJ5IHN1cHBvcnRzIHR3byBkaWFsZWN0cyBvZiB0aGUgY29kaW5nIGxhbmd1YWdlIFNRTCwgYm90aCBzdGFuZGFyZCBhbmQgbGVnYWN5LiBUaGlzIGlzIHRoZSBxdWVyeSB1c2VkIHRvIHNlbGVjdCB0aGUgdmFyaWFibGVzIHdlIHdpbGwgYmUgd29ya2luZyB3aXRoLiBBZnRlciBydW5uaW5nIHRoZSBxdWVyeSB5b3UgY2FuIGRvd25sb2FkIHRoZSBkYXRhIHRvIHJ1biBhbmFseXNpcyBvbi4NCg0KIVtdKEM6L1VzZXJzL0NTRmljL0Rlc2t0b3AvR29vZ2xlIENsb3VkLlBORykNCg0KIyMgX19EYXRhIFRhYmxlX18NCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9RkFMU0V9DQojIGltcG9ydCB0aGUgZGF0YQ0KZGYgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL0NTRmljL0Rlc2t0b3AvYnEtcmVzdWx0cy0yMDIyMDExNi0xODMwMDYtcDI2am9pYnIxN3FiLmNzdiIpDQoNCmBgYA0KDQoNCg0KfCAgIEJlbG93IGlzIGEgc2FtcGxlIHZpZXcgb2YgdGhlIGRhdGEgdGhhdCB3YXMgaW1wb3J0ZWQuDQoNCjxicj4NCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpoZWFkKGRmKSANCmBgYA0KDQp8ICAgICBOb3cgdGhhdCB3ZSBoYXZlIGRhdGEsIHdlIG5lZWQgdG8gYW5hbHl6ZSBpdC4gVG8gZG8gdGhpcyB3ZSBuZWVkIHRvIG1ha2Ugc3VyZSBpdCBpcyBjb21wbGV0ZSBhbmQgb2YgdGhlIHByb3BlciB0eXBlLiBKdXN0IGxvb2tpbmcgYXQgdGhlIHRhYmxlIHdlIHNlZSB0aGF0IHR3byBvZiB0aGUgdmFyaWFibGVzIGFyZSBvZiB0aGUgY2hhcmFjdGVyIHR5cGUuIFNpbmNlIHRoZXNlIGNvbHVtbnMgb25seSBoYXZlIHR3byByZXNwb25zZXMsIHRoZSBkYXRhIHdpbGwgd29yayBiZXR0ZXIgYXMgYmluYXJ5Lg0KDQo8YnI+DQoNCkNoZWNrIGRhdGEgZm9yIG1pc3Npbmcgb2JzZXJ2YXRpb25zLg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KEFtZWxpYSkNCm1pc3NtYXAoZGYpDQpgYGANCjxicj4gDQoNCnwgICAgVGhlcmUgaXNuJ3QgYW55IG1pc3Npbmcgb2JzZXJ2YXRpb25zIGluIHRoZSBlbnRpcmUgZGF0YSBmcmFtZS4gTmV4dCBzdGVwIHdpbGwgYmUgdG8gYWx0ZXIgdGhlIGRhdGEgdHlwZXMgc28gd2UgY2FuIHdvcmsgd2l0aCBpdCBhIGxpdHRsZSBlYXNpZXIuIFRoaXMgY2FuIGJlIGRvbmUgYnkgY29udmVydGluZyB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGZyb20gY2hhcmFjdGVyIGludG8gYmluYXJ5IGZvcm1hdCB0byBtYWtlIHRoZSBvYnNlcnZhdGlvbnMgbnVtZXJpYy4NCg0KPGJyPg0KDQpgYGB7cn0NCiMgaWYtZWxzZSBzdGF0ZW1lbnQgdG8gbWFrZSBvYnNlcnZhdGlvbnMgbW9yZSB0aGFuIDUwSyBlcXVhbCB0byAxLCBsZXNzIHRoYW4gNTBLIHRvIDAsIGFuZCBjb252ZXJ0IHRvIGludGVnZXIuDQpkZiRpbmNvbWVfYnJhY2tldF9iaW4gPC0gYXMuaW50ZWdlcihpZmVsc2UoZGYkaW5jb21lX2JyYWNrZXQgPT0gIiA+NTBLIiwgIjEiLCAiMCIpKQ0KDQojIGlmLWVsc2Ugc3RhdGVtZW50IHRvIG1ha2Ugb2JzZXJ2YXRpb25zIG1hbGUgZXF1YWwgdG8gMSwgZmVtYWxlIHRvIDAsIGFuZCBjb252ZXJ0IHRvIGludGVnZXIuDQpkZiRzZXhfYmluIDwtIGFzLmludGVnZXIoaWZlbHNlKGRmJHNleCA9PSAiIE1hbGUiLCAiMSIsICIwIikpDQoNCmBgYA0KDQo8YnI+DQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KaGVhZChkZikNCmBgYA0KDQo8YnI+DQoNClRoZSBkYXRhIG5vdyBzZWVtcyB0byBiZSBpbiBvcmRlci4gTm93LCB0byB2aXN1YWxpemUgdGhlIGRhdGEuDQoNCg0KDQojIyBWaXN1YWwgUmVwcmVzZW50YXRpb24gb2YgRGF0YSB7LnRhYnNldH0NCg0KIyMjIFZpb2xpbiBQbG90DQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCg0KdiA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IHNleCwgeSA9IGVkdWNhdGlvbl9udW0sIGNvbG9yPXNleCwgZmlsbD1zZXgpKSAgKyANCiAgICAgICAgZ2VvbV92aW9saW4oKQ0KDQoNCnYgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInJlZCIsICJibHVlIikpDQogIA0KYGBgDQoNCg0KIyMjIFN0YWNrZWQgQmFycGxvdA0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmIgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBlZHVjYXRpb25fbnVtLCBmaWxsID0gc2V4KSkgKw0KICAgICAgICBnZW9tX2JhcigpDQpiICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJyZWQiLCAiYmx1ZSIpKQ0KYGBgDQoNCg0KIyBfX0FuYWx5c2lzX18NCg0KfCAgICAgRm9yIGZ1biwgd2UgY2FuIHRyeSB0byBwcmVkaWN0IHRoZSBpbmNvbWUgYnJhY2tldCBwZW9wbGUgd2lsbCBmYWxsIGludG8gYmFzZWQgb24gc2V4IGFuZCBlZHVjYXRpb24gdXNpbmcgYSBmZXcgZGlmZmVyZW50IG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLiBUbyBkbyBzbywgd2Ugd2lsbCBuZWVkIHRvIHNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3RpbmcuICBJIG9yZ2FuaXplZCB0aGUgdmlzdWFscyBmb3IgdGhlIGRpZmZlcmVudCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyBpbiBkZWNyZWFzaW5nIG9yZGVyIGJ5IGFjY3VyYWN5Lg0KDQoNCiMjIE1hY2hpbmUgbGVhcm5pbmcgVmlzdWFscyB7LnRhYnNldH0NCg0KIyMjIE5ldXJhbCBOZXR3b3JrIFBsb3QNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShuZXVyYWxuZXQpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShkcGx5cikNCg0KI25vcm1hbGl6ZQ0Kbm9ybWFsaXplID0gZnVuY3Rpb24oeCkgew0KcmV0dXJuKCh4LW1pbih4KSkvKG1heCh4KS1taW4oeCkpKQ0KfQ0KDQpkZjIgPSBkZiAlPiUgbXV0YXRlX2F0KDM6NSwgbm9ybWFsaXplKQ0KDQojIGNyZWF0ZSB0ZXN0IGFuZCB0cmFpbiBzZXRzDQpzZXQuc2VlZCgxKQ0KdHJhaW5fcm93cyA9IGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IGRmMiRpbmNvbWVfYnJhY2tldCwgcCA9IDAuODAsIGxpc3QgPSBGQUxTRSkNCnRyYWluID0gZGYyW3RyYWluX3Jvd3MsXQ0KdGVzdCA9IGRmMlstdHJhaW5fcm93cyxdDQoNCiMgZml0IG5ldXJhbCBuZXR3b3JrDQpzZXQuc2VlZCg0MikNCk5OID0gbmV1cmFsbmV0KGluY29tZV9icmFja2V0X2JpbiB+IHNleF9iaW4gKyBlZHVjYXRpb25fbnVtLCBkYXRhID0gdHJhaW4sIGhpZGRlbiA9IDIsIGxpbmVhci5vdXRwdXQgPSBUUlVFLCB0aHJlc2hvbGQ9MC4wMSkNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgcGxvdCBuZXVyYWwgbmV0d29yaw0KcGxvdChOTiwgcmVwID0gImJlc3QiKQ0KYGBgDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgcHJlZGljdGlvbnMNCnByZWQgPSBwcmVkaWN0KE5OLCB0ZXN0KQ0KcHJlZF9iaW4gPC0gYXMuZmFjdG9yKGlmZWxzZShwcmVkID4gMC41LCAiMSIsICIwIikpDQoNCiMgbWF0cml4DQpjb25mdXNpb25NYXRyaXgoZmFjdG9yKHByZWRfYmluKSwgZmFjdG9yKHRlc3QkaW5jb21lX2JyYWNrZXRfYmluKSwgbW9kZSA9ICJwcmVjX3JlY2FsbCIsIHBvc2l0aXZlID0gIjEiKQ0KYGBgDQoNCiMjIyBEZWNpc2lvbiBUcmVlDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCnNldC5zZWVkKDQyKQ0KIyB1c2luZyBvcmlnaW5hbCBkZiBsYWJlbHMNCnRyZWUgPSBycGFydChpbmNvbWVfYnJhY2tldCB+IHNleCArIGVkdWNhdGlvbl9udW0sIGRhdGEgPSB0cmFpbiwgbWV0aG9kID0gImNsYXNzIiwgcGFybXMgPSBsaXN0KHNwbGl0ID0gImluZm9ybWF0aW9uIikpDQpycGFydC5wbG90KHRyZWUsIGJveC5wYWxldHRlPSJSZEJ1Iiwgc2hhZG93LmNvbD0iZ3JheSIsIG5uPVRSVUUpDQoNCiMgYmluYXJ5IG1vZGVsDQp0cmVlMiA9IHJwYXJ0KGluY29tZV9icmFja2V0X2JpbiB+IHNleF9iaW4gKyBlZHVjYXRpb25fbnVtLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHJlZF90cmVlID0gcHJlZGljdCh0cmVlMiwgdGVzdCwgdHlwZSA9ICJjbGFzcyIpDQpjb25mdXNpb25NYXRyaXgoZmFjdG9yKHByZWRfdHJlZSksIGZhY3Rvcih0ZXN0JGluY29tZV9icmFja2V0X2JpbikpDQpgYGANCg0KDQoNCiMjIyBOYWl2ZSBCYXllcyBQbG90DQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkobmFpdmViYXllcykNCg0KTkJfbW9kZWwgPC0gbmFpdmVfYmF5ZXMoZmFjdG9yKGluY29tZV9icmFja2V0X2JpbikgfiBzZXhfYmluICsgZWR1Y2F0aW9uX251bSwgZGF0YSA9IHRyYWluLCB1c2VrZXJuZWwgPSBUUlVFKSANCnBsb3QoTkJfbW9kZWwpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpwcmVkX25iID0gcHJlZGljdChOQl9tb2RlbCwgdGVzdCwgdHlwZT0iY2xhc3MiKQ0KY29uZnVzaW9uTWF0cml4KHByZWRfbmIsIGZhY3Rvcih0ZXN0JGluY29tZV9icmFja2V0X2JpbiksbW9kZSA9ICJwcmVjX3JlY2FsbCIsIHBvc2l0aXZlID0gIjEiKQ0KYGBgDQp8ICAgICBXZSBjYW4gc29tZXdoYXQgYWNjdXJhdGVseSBwcmVkaWN0IGluY29tZSBicmFja2V0IGJhc2VkIG9uIGdlbmRlciBhbmQgZWR1Y2F0aW9uIGxldmVsLiBUaGlzIG1lYW5zIHRoYXQgdGhlcmUgaXMgc29tZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMuIE5vcm1hbGx5IHlvdSBjb3VsZCBqdXN0IHVzZSB0aGUgcGFpcnMgZnVuY3Rpb24gdG8gdmlldyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzLCBidXQgc2luY2UgdGhlIHZhcmlhYmxlcyBhcmUgbW9zdGx5IGJpbmFyeSBpdCB3aWxsIG5vdCBoYXZlIG11Y2ggcmVhZGFiaWxpdHkuIFRoZSBxdWVzdGlvbiBJIGFtIG1vc3QgY3VyaW91cyBhYm91dCB3aXRoIHRoaXMgZGF0YSwgdGhhdCBoYXMgbm90IGJlZW4gYW5zd2VyZWQgeWV0LCB3b3VsZCBiZSB0aGUgcHJvcG9ydGlvbnMgb2YgdGhlIHR3byB2YXJpYWJsZXMgdGhhdCBhcmUgaW4gYSBpbmNvbWUgYnJhY2tldCBhYm92ZSBmaWZ0eS10aG91c2FuZC4gVGhpcyBtYXkgYmUgYmVzdCBvYnNlcnZlZCB3aXRoIHRoZSB1c2Ugb2YgdGFibGVzLg0KDQoNCiMjIFByb3BvcnRpb24gVGFibGVzIHsudGFic2V0fQ0KDQojIyMgR2VuZGVyIG9mIEluY29tZSBBYm92ZSA1MEsNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHJpbnQoJ0NvdW50cycpDQp0YWJsZShkZiRpbmNvbWVfYnJhY2tldD09JyA+NTBLJywgZGYkc2V4KQ0KcHJpbnQoJ1BlcmNlbnRhZ2VzJykNCnByb3AudGFibGUodGFibGUoZGYkaW5jb21lX2JyYWNrZXQ9PScgPjUwSycsIGRmJHNleCkpKjEwMA0KYGBgDQpfX0NvbmNsdXNpb25fXw0KDQp8ICAgICBOZWFybHkgaGFsZiBvZiB0aGUgb2JzZXJ2YXRpb25zIGFyZSBtYWxlIGFib3ZlIDUwSyBpbmNvbWUgbGV2ZWwuIFRoZXJlIGlzIHNvbWUgYmlhcyBoZXJlIGFzIHRoZSBwcm9wb3J0aW9uIG9mIG1lbiB0byB3b21lbiBoYXMgYSByYXRpbyBvZiBhbG1vc3QgMjoxLiBUaGUgcmF0aW9zIG9mIHNleCBpbiB0aGUgaGlnaGVyIGluY29tZSBkaWZmZXIgcXVpdGUgYSBiaXQuIEZvciBmZW1hbGUsIGl0IGlzIDE6MTAgb2YgPjUwSyB0byA8PTUwSy4gRm9yIE1hbGUsIGl0IGlzIDE6Mi4gVGhpcyBtZWFucyB0aGF0IGZvciB0aGUgZ2VuZGVycywgb25seSBhIDEwdGggb2YgdGhlIGZlbWFsZSBwb3B1bGF0aW9uIG1ha2UgYSBncmVhdCB3YWdlIHdoaWxlIG5lYXJseSBhIHRoaXJkIG9mIHRoZSBtYWxlIHBvcHVsYXRpb24gaXMgYWJvdmUgNTBLIG9mIGluY29tZS4NCg0KDQojIyMgRWR1Y2F0aW9uIExldmVsIG9mIEluY29tZSBBYm92ZSA1MEsNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHJpbnQoJ0NvdW50cycpDQp0YWJsZShkZiRpbmNvbWVfYnJhY2tldD09JyA+NTBLJywgZGYkZWR1Y2F0aW9uX251bSkNCg0KcHJpbnQoJ1BlcmNlbnRhZ2VzJykNCnByb3AudGFibGUodGFibGUoZGYkaW5jb21lX2JyYWNrZXQ9PScgPjUwSycsIGRmJGVkdWNhdGlvbl9udW0pKSoxMDANCmBgYA0KX19Db25jbHVzaW9uX18NCg0KfCAgICAgTW9zdCBvZiB0aGUgcG9wdWxhdGlvbiBoYXMgYSBlZHVjYXRpb24gbGV2ZWwgYmV0d2VlbiA5IGFuZCAxNC4gRm9yIHRoZSBtb3N0IHBhcnQsIHRoZSByYXRpbyBmb3IgdGhlIHNhbWUgZWR1Y2F0aW9uIG51bWJlcnMgYXJlIDE6NCwgdGhlIDEgYmVpbmcgdGhvc2UgaW4gdGhlIGluY29tZSBicmFja2V0IGFib3ZlIDUwSy4NCg0KDQojIyMgRWR1Y2F0aW9uIExldmVsIG9mIEluY29tZSBBYm92ZSA1MEsgYW5kIE1hbGUNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHJpbnQoJ0NvdW50cycpDQp0YWJsZShkZiRpbmNvbWVfYnJhY2tldD09JyA+NTBLJyAmIGRmJHNleCA9PSAnIE1hbGUnLCBkZiRlZHVjYXRpb25fbnVtKQ0KcHJpbnQoJ1BlcmNlbnRhZ2VzJykNCnByb3AudGFibGUodGFibGUoZGYkaW5jb21lX2JyYWNrZXQ9PScgPjUwSycgJiBkZiRzZXggPT0gJyBNYWxlJywgZGYkZWR1Y2F0aW9uX251bSkpKjEwMA0KYGBgDQpfX0NvbmNsdXNpb25fXw0KDQp8ICAgICBGb3IgdGhlIG1hbGUgcG9wdWxhdGlvbiBhYm92ZSA1MEssIHRoZSBtYWpvcml0eSBmYWxsIG9uIGVkdWNhdGlvbiBudW1iZXJzIG9mIDkgYW5kIDEzLg0KDQoNCg0KIyMjIEVkdWNhdGlvbiBMZXZlbCBvZiBJbmNvbWUgQWJvdmUgNTBLIGFuZCBGZW1hbGUNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHJpbnQoJ0NvdW50cycpDQp0YWJsZShkZiRpbmNvbWVfYnJhY2tldD09JyA+NTBLJyAmIGRmJHNleCA9PSAnIEZlbWFsZScsIGRmJGVkdWNhdGlvbl9udW0pDQpwcmludCgnUGVyY2VudGFnZXMnKQ0KcHJvcC50YWJsZSh0YWJsZShkZiRpbmNvbWVfYnJhY2tldD09JyA+NTBLJyAmIGRmJHNleCA9PSAnIEZlbWFsZScsIGRmJGVkdWNhdGlvbl9udW0pKSoxMDANCmBgYA0KX19Db25jbHVzaW9uX18NCg0KfCAgICAgRm9yIHRoZSBmZW1hbGUgcG9wdWxhdGlvbiBhYm92ZSA1MEssIHRoZSBtYWpvcml0eSBmYWxsIG9uIGVkdWNhdGlvbiBudW1iZXJzIG9mIDkgYW5kIDEzIHNpbWlsYXIgdG8gdGhlIG1hbGVzLiBUaGUgcGVyY2VudGFnZXMgYXJlIG11Y2ggc21hbGxlciwgYnV0IHRoYXQgd2FzIGV4cGVjdGVkIGZyb20gdGhlIGVhcmxpZXIgdGFibGUgY29uY2x1c2lvbnMuDQoNCg0KIyBfX0Nsb3NpbmdfXw0KDQp8ICAgICBJIGhvcGUgeW91IGVuam95ZWQgdGhpcyB3cml0ZS11cCBhbmQgbGVhcm5lZCBzb21ldGhpbmcgZnJvbSBpdC4gSWYgeW91IHdpc2ggdG8gc2VlIHNvbWUgb2YgbXkgb3RoZXIgcHJvamVjdHMgeW91IGNhbiBjaGVjayBvdXQgbXkgcG9ydGZvbGlvIHdlYnNpdGUsIGdpdGh1Yiwgb3IgTGlua2VkaW4uDQoNCiFbW1BvcnRmb2xpb10oaHR0cHM6Ly9naXRodWIuY29tL0NyYWlnLUZpY2spXQ0KDQohW1tHaXRodWJdKGh0dHBzOi8vY3JhaWdmaWNrLm15cG9ydGZvbGlvLmNvbS9wcm9qZWN0cyldDQoNCiFbW0xpbmtlZGluXShodHRwczovL3d3dy5saW5rZWRpbi5jb20vaW4vY3JhaWctZmljay8pXQ0KDQojIF9fU2Vzc2lvbiBJbmZvX18NCg0KYGBge3J9DQpzZXNzaW9uSW5mbygpDQpgYGANCg0K