Get Me BIG DATA, Stat!


GOOGLE IMAGES

1 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!

2 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.

3 Data

The data I will use for the project is the Census_adult_income, which is located in the ml_datasets.


3.1 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.


List of all of the data variables, types, and definitions of the set.

3.2 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.

3.3 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.

3.4 Visual Representation of Data

3.4.1 Violin Plot

3.4.2 Stacked Barplot

4 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.

4.1 Machine learning Visuals

4.1.1 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               
## 

4.1.2 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             
## 

4.1.3 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.

4.2 Proportion Tables

4.2.1 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.

4.2.2 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.

4.2.3 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.

4.2.4 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.

5 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]

6 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