Introduction


Association rule learning is a data analysis technique in machine learning that reveals relationships between variables in a large dataset. The basic idea is to find common patterns of elements that occur together in transactions or records. These patterns are then used to generate rules, called association rules, that suggest a relationship between elements. The use of association rule learning is significant in data mining as it provides insights into the connections between variables in the data. This information can then be used to make informed choices and forecast future trends. By discovering hidden patterns in the data, organizations can also improve their efficiency and make more informed decisions.

The following analysis is an example of the use of association rules in practice.

About Dataset


The data used in the analysis was taken from Kaggle: https://www.kaggle.com/datasets/wenruliu/adult-income-dataset

The original dataset named ‘adult’ came from the UCI machine learning repository: https://archive.ics.uci.edu/ml/datasets/adult

Before cleaning, the data contains 15 variables (6 continuous and 9 nominal/ordinal) and 48842 observations. The database contains information on the individuals income and personal information. These include age, gender, race, country of origin, marital status and others. All variables and their descriptions can be found in the tables below.

  • Continuous attributes
Variable Description
age age of respondent
fnlwgt represents final weight, which is the number of units in the target population that the responding unit represents
education.num number of years of education in total
capital.gain income from investment sources other than wage/salary
capital.loss losses from investment sources
hours.per.week number of hours worked per week


  • Nominal/ordinal attributes
Variable Description
workclass Private, Self-emp-not-inc, Self-emp-inc, Federal-gov, Local-gov, State-gov, Without-pay, Never-worked
education Bachelors, Some-college, 11th, HS-grad, Prof-school, Assoc-acdm, Assoc-voc, 9th, 7th-8th, 12th, Masters, 1st-4th, 10th, Doctorate, 5th-6th, Preschool
marital.status Married-civ-spouse, Divorced, Never-married, Separated, Widowed, Married-spouse-absent, Married-AF-spouse
occupation Tech-support, Craft-repair, Other-service, Sales, Exec-managerial, Prof-specialty, Handlers-cleaners, Machine-op-inspct, Adm-clerical, Farming-fishing, Transport-moving, Priv-house-serv, Protective-serv, Armed-Forces
relationship Wife, Own-child, Husband, Not-in-family, Other-relative, Unmarried
race White, Asian-Pac-Islander, Amer-Indian-Eskimo, Other, Black
gender Female, Male
native.country United-States, Cambodia, England, Puerto-Rico, Canada, Germany, Outlying-US(Guam-USVI-etc), India, Japan, Greece, South, China, Cuba, Iran, Honduras, Philippines, Italy, Poland, Jamaica, Vietnam, Mexico, Portugal, Ireland, France, Dominican-Republic, Laos, Ecuador, Taiwan, Haiti, Columbia, Hungary, Guatemala, Nicaragua, Scotland, Thailand, Yugoslavia, El-Salvador, Trinadad&Tobago, Peru, Hong, Holand-Netherlands
income >50K, <=50K

Loading data

data<-read.csv("adult.csv", header=TRUE, sep=",")
head(data)

Data cleaning and preprocessing

In order to apply association rules methods, variables need to be prepared. The data must be in transactional format. It is therefore necessary to convert continuous variables into nominal variables. We can get rid of some variables such as “fnlwgt”, “educational.num”, as they do not provide valuable information for analysis. The remaining continuous variables need to be grouped and converted into a factor variable type.

data <- select(data,-c("fnlwgt", "educational.num"))
data[!complete.cases(data),]
data %>% group_by(education) %>% tally()
data %>% group_by(workclass) %>% tally()
data %>% group_by(marital.status) %>% tally()
data %>% group_by(occupation) %>% tally()
data %>% group_by(relationship) %>% tally()
data %>% group_by(race) %>% tally()
data %>% group_by(gender) %>% tally()
data %>% group_by(native.country) %>% tally()
data %>% group_by(income) %>% tally()
data <- data %>% mutate(workclass = ifelse(workclass == "?", "Unknown_workclass", workclass))
data <- data %>% mutate(occupation = ifelse(occupation == "?", "Unknown_occupation", occupation))
data <- data %>% mutate(native.country = ifelse(native.country == "?", "Unknown_native.country", native.country))
str(data)
'data.frame':   48842 obs. of  13 variables:
 $ age           : int  25 38 28 44 18 34 29 63 24 55 ...
 $ workclass     : chr  "Private" "Private" "Local-gov" "Private" ...
 $ education     : chr  "11th" "HS-grad" "Assoc-acdm" "Some-college" ...
 $ marital.status: chr  "Never-married" "Married-civ-spouse" "Married-civ-spouse" "Married-civ-spouse" ...
 $ occupation    : chr  "Machine-op-inspct" "Farming-fishing" "Protective-serv" "Machine-op-inspct" ...
 $ relationship  : chr  "Own-child" "Husband" "Husband" "Husband" ...
 $ race          : chr  "Black" "White" "White" "Black" ...
 $ gender        : chr  "Male" "Male" "Male" "Male" ...
 $ capital.gain  : int  0 0 0 7688 0 0 0 3103 0 0 ...
 $ capital.loss  : int  0 0 0 0 0 0 0 0 0 0 ...
 $ hours.per.week: int  40 50 40 40 30 30 40 32 40 10 ...
 $ native.country: chr  "United-States" "United-States" "United-States" "United-States" ...
 $ income        : chr  "<=50K" "<=50K" ">50K" ">50K" ...
ggplot(data, aes(age)) +
  geom_histogram(aes(y = ..density..), color = "#000000", fill = "#0099F8") +
  geom_density(color = "#000000", fill = "#F85700", alpha = 0.6)

max(data$age)
[1] 90
min(data$age)
[1] 17
data$age <- cut(data$age,
            breaks = c(0,24,34,44,54,64,74,100),
            labels = c("17-24","25-34","35-44","45-54","55-64","65-74","75+"),
            ordered_result = T)

class(data$age)
[1] "ordered" "factor" 
data %>% group_by(age) %>% tally()
ggplot(data, aes(hours.per.week)) +
  geom_histogram(aes(y = ..density..), color = "#000000", fill = "#0099F8") +
  geom_density(color = "#000000", fill = "#F85700", alpha = 0.6)

max(data$hours.per.week)
[1] 99
min(data$hours.per.week)
[1] 1
table(data$hours.per.week)

    1     2     3     4     5     6     7     8     9    10    11    12    13    14    15    16    17    18    19    20 
   27    53    59    84    95    92    45   218    27   425    20   247    28    55   623   303    42   129    19  1862 
   21    22    23    24    25    26    27    28    29    30    31    32    33    34    35    36    37    38    39    40 
   46    62    40   354   958    40    43   140    15  1700    12   423    61    48  1937   336   242   714    63 22803 
   41    42    43    44    45    46    47    48    49    50    51    52    53    54    55    56    57    58    59    60 
   59   338   227   310  2717   129    82   770    39  4246    20   205    39    62  1051   141    19    38     7  2177 
   61    62    63    64    65    66    67    68    69    70    72    73    74    75    76    77    78    79    80    81 
    4    23    15    22   355    23     6    16     1   437   107     4     3   105     4     9    13     1   210     3 
   82    84    85    86    87    88    89    90    91    92    94    95    96    97    98    99 
    1    72    17     4     1     4     3    42     3     3     1     2     9     2    14   137 
data$hours.per.week <- cut(data$hours.per.week,
                breaks = c(0,31,40,60,100),
                labels = c("Working_part-time","Working_full-time","Working_long-hours","Possible_workaholic"),
                ordered_result = T)

class(data$hours.per.week)
[1] "ordered" "factor" 
data %>% group_by(hours.per.week) %>% tally()
ggplot(data, aes(capital.gain)) +
  geom_histogram(aes(y = ..density..), color = "#000000", fill = "#0099F8") +
  geom_density(color = "#000000", fill = "#F85700", alpha = 0.6)

max(data$capital.gain)
[1] 99999
min(data$capital.gain)
[1] 0
table(data$capital.gain)

    0   114   401   594   914   991  1055  1086  1111  1151  1173  1264  1409  1424  1455  1471  1506  1639  1731  1797 
44807     8     5    52    10     6    37     8     1    13     5     2    10     4     4     9    24     1     1    10 
 1831  1848  2009  2036  2050  2062  2105  2174  2176  2202  2228  2290  2329  2346  2354  2387  2407  2414  2463  2538 
    9     9     3     5     5     3    15    74    31    28     5    10     7     8    21     1    25    10    15     5 
 2580  2597  2635  2653  2829  2885  2907  2936  2961  2964  2977  2993  3103  3137  3273  3325  3411  3418  3432  3456 
   20    31    14    11    42    30    18     4     4    14    11     3   152    51     7    81    34     8     4     6 
 3464  3471  3674  3781  3818  3887  3908  3942  4064  4101  4386  4416  4508  4650  4687  4787  4865  4931  4934  5013 
   33    11    22    16    11     8    42    18    54    29   108    24    23    63     4    35    25     4    10   117 
 5060  5178  5455  5556  5721  6097  6360  6418  6497  6514  6612  6723  6767  6849  7262  7298  7430  7443  7688  7896 
    2   146    18     6     7     2     3    16    15    10     1     5     6    42     1   364    15     7   410     4 
 7978  8614  9386  9562 10520 10566 10605 11678 13550 14084 14344 15020 15024 15831 18481 20051 22040 25124 25236 27828 
    2    82    31     5    64     8    19     4    42    49    34    10   513     8     2    49     1     6    14    58 
34095 41310 99999 
    6     3   244 
x <- data %>% filter(capital.gain > 0)

ggplot(x, aes(capital.gain)) +
  geom_histogram(aes(y = ..density..), color = "#000000", fill = "#0099F8") +
  geom_density(color = "#000000", fill = "#F85700", alpha = 0.6)

max(x$capital.gain)
[1] 99999
min(x$capital.gain)
[1] 114
table(x$capital.gain)

  114   401   594   914   991  1055  1086  1111  1151  1173  1264  1409  1424  1455  1471  1506  1639  1731  1797  1831 
    8     5    52    10     6    37     8     1    13     5     2    10     4     4     9    24     1     1    10     9 
 1848  2009  2036  2050  2062  2105  2174  2176  2202  2228  2290  2329  2346  2354  2387  2407  2414  2463  2538  2580 
    9     3     5     5     3    15    74    31    28     5    10     7     8    21     1    25    10    15     5    20 
 2597  2635  2653  2829  2885  2907  2936  2961  2964  2977  2993  3103  3137  3273  3325  3411  3418  3432  3456  3464 
   31    14    11    42    30    18     4     4    14    11     3   152    51     7    81    34     8     4     6    33 
 3471  3674  3781  3818  3887  3908  3942  4064  4101  4386  4416  4508  4650  4687  4787  4865  4931  4934  5013  5060 
   11    22    16    11     8    42    18    54    29   108    24    23    63     4    35    25     4    10   117     2 
 5178  5455  5556  5721  6097  6360  6418  6497  6514  6612  6723  6767  6849  7262  7298  7430  7443  7688  7896  7978 
  146    18     6     7     2     3    16    15    10     1     5     6    42     1   364    15     7   410     4     2 
 8614  9386  9562 10520 10566 10605 11678 13550 14084 14344 15020 15024 15831 18481 20051 22040 25124 25236 27828 34095 
   82    31     5    64     8    19     4    42    49    34    10   513     8     2    49     1     6    14    58     6 
41310 99999 
    3   244 
data$capital.gain <- cut(data$capital.gain,
                           breaks = c(-1,0,median(x$capital.gain),max(x$capital.gain)),
                           labels = c("0_capital_gain","below_median_capital_gain","above_median_capital_gain"),
                           ordered_result = T)

class(data$capital.gain)
[1] "ordered" "factor" 
data %>% group_by(capital.gain) %>% tally()
y <- data %>% filter(capital.loss > 0)

ggplot(data, aes(capital.loss)) +
  geom_histogram(aes(y = ..density..), color = "#000000", fill = "#0099F8") +
  geom_density(color = "#000000", fill = "#F85700", alpha = 0.6)

max(data$capital.loss)
[1] 4356
min(data$capital.loss)
[1] 0
table(data$capital.loss)

    0   155   213   323   419   625   653   810   880   974  1092  1138  1258  1340  1380  1408  1411  1421  1429  1485 
46560     1     5     5     3    17     4     2     6     2    11     4     6    11    10    35     4     1     3    71 
 1504  1510  1539  1564  1573  1579  1590  1594  1602  1617  1628  1648  1651  1668  1669  1672  1719  1721  1726  1735 
   26     3     1    43    12    30    62     9    62    11    24     3    11     9    35    50    38    28     9     3 
 1740  1741  1755  1762  1816  1825  1844  1848  1870  1876  1887  1902  1911  1944  1974  1977  1980  2001  2002  2042 
   58    44     2    20     4     5     3    67     1    59   233   304     1     3    28   253    36    35    33    12 
 2051  2057  2080  2129  2149  2163  2174  2179  2201  2205  2206  2231  2238  2246  2258  2267  2282  2339  2352  2377 
   29    16     1     7     5     2    10    20     1    19     6     7     4     8    39     3     2    27     2    25 
 2392  2415  2444  2457  2465  2467  2472  2489  2547  2559  2603  2754  2824  3004  3175  3683  3770  3900  4356 
   11    72    20     4     1     2     4     1     5    17     7     2    14     5     2     2     4     2     3 
data$capital.loss <- cut(data$capital.loss,
                         breaks = c(-1,0,median(y$capital.loss),max(y$capital.loss)),
                         labels = c("0_capital_loss","below_median_capital_loss","above_median_capital_loss"),
                         ordered_result = T)

class(data$capital.loss)
[1] "ordered" "factor" 
data %>% group_by(capital.loss) %>% tally()

Due to the asymmetric distribution of the variable capital.gain and capital.loss, the median instead of the average of all non-zero income/losses respectively was used in determining the new levels.

summary(data)
    age         workclass          education         marital.status      occupation        relationship      
 17-24: 8432   Length:48842       Length:48842       Length:48842       Length:48842       Length:48842      
 25-34:12577   Class :character   Class :character   Class :character   Class :character   Class :character  
 35-44:12193   Mode  :character   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 45-54: 8771                                                                                                 
 55-64: 4782                                                                                                 
 65-74: 1642                                                                                                 
 75+  :  445                                                                                                 
     race              gender                             capital.gain                      capital.loss  
 Length:48842       Length:48842       0_capital_gain           :44807   0_capital_loss           :46560  
 Class :character   Class :character   below_median_capital_gain: 2345   below_median_capital_loss: 1166  
 Mode  :character   Mode  :character   above_median_capital_gain: 1690   above_median_capital_loss: 1116  
                                                                                                          
                                                                                                          
                                                                                                          
                                                                                                          
             hours.per.week  native.country        income         
 Working_part-time  : 7863   Length:48842       Length:48842      
 Working_full-time  :26627   Class :character   Class :character  
 Working_long-hours :12676   Mode  :character   Mode  :character  
 Possible_workaholic: 1676                                        
                                                                  
                                                                  
                                                                  
trans <- transactions(data)
Warning: Column(s) 2, 3, 4, 5, 6, 7, 8, 12, 13 not logical or factor. Applying default discretization (see '? discretizeDF').
length(trans)
[1] 48842
LIST(head(trans, 2))
$`1`
 [1] "age=25-34"                        "workclass=Private"                "education=11th"                  
 [4] "marital.status=Never-married"     "occupation=Machine-op-inspct"     "relationship=Own-child"          
 [7] "race=Black"                       "gender=Male"                      "capital.gain=0_capital_gain"     
[10] "capital.loss=0_capital_loss"      "hours.per.week=Working_full-time" "native.country=United-States"    
[13] "income=<=50K"                    

$`2`
 [1] "age=35-44"                         "workclass=Private"                 "education=HS-grad"                
 [4] "marital.status=Married-civ-spouse" "occupation=Farming-fishing"        "relationship=Husband"             
 [7] "race=White"                        "gender=Male"                       "capital.gain=0_capital_gain"      
[10] "capital.loss=0_capital_loss"       "hours.per.week=Working_long-hours" "native.country=United-States"     
[13] "income=<=50K"                     

The finished data prepared for further analysis was saved under the name ‘trans’. The number of transactions is 48842, with a fixed length of 13.

Analysis

Item frequency

head(sort(itemFrequency(trans, type="absolute"), decreasing = T), 20)
      capital.loss=0_capital_loss       capital.gain=0_capital_gain      native.country=United-States 
                            46560                             44807                             43832 
                       race=White                      income=<=50K                 workclass=Private 
                            41762                             37155                             33906 
                      gender=Male  hours.per.week=Working_full-time marital.status=Married-civ-spouse 
                            32650                             26627                             22379 
             relationship=Husband                     gender=Female      marital.status=Never-married 
                            19716                             16192                             16117 
                education=HS-grad hours.per.week=Working_long-hours        relationship=Not-in-family 
                            15784                             12676                             12583 
                        age=25-34                         age=35-44                       income=>50K 
                            12577                             12193                             11687 
           education=Some-college                         age=45-54 
                            10878                              8771 
head(sort(itemFrequency(trans, type="absolute"), decreasing = F), 20)
        native.country=Holand-Netherlands                    workclass=Never-worked 
                                        1                                        10 
                  occupation=Armed-Forces                    native.country=Hungary 
                                       15                                        19 
                  native.country=Honduras                     workclass=Without-pay 
                                       20                                        21 
                  native.country=Scotland                       native.country=Laos 
                                       21                                        23 
native.country=Outlying-US(Guam-USVI-etc)                 native.country=Yugoslavia 
                                       23                                        23 
           native.country=Trinadad&Tobago                   native.country=Cambodia 
                                       27                                        28 
                      native.country=Hong                   native.country=Thailand 
                                       30                                        30 
         marital.status=Married-AF-spouse                    native.country=Ireland 
                                       37                                        37 
                    native.country=France                    native.country=Ecuador 
                                       38                                        45 
                      native.country=Peru                     native.country=Greece 
                                       46                                        49 
itemFrequencyPlot(trans, topN=20, type="relative", main="Frequency plot")

The chart above shows the most frequently occurring values in the dataset. As you can see, there are several values that appear in almost every ‘transaction’. For example, the frequency of capital.loss=0 is over 90%. This should not come as a surprise, as it was already visible in the data analysis. The same is true for the values native.country=United-States and race=White. Again, this is not surprising as the data relates to people from the United States, where white people made up the vast majority of the population at the time of data collection.

It is therefore to be expected that these most common values will appear with the vast majority of rules.

Measuring rule interest

  • Support of an association rule is the percentage of groups containing all elements in the rule, calculated from the total number of groups examined. It represents the frequency of occurrence of the rule’s body and head. Setting a minimum support level when mining can produce more significant results and control the number of rules generated.

  • Confidence measures the reliability of an association rule by indicating the frequency at which the head of the rule appears in groups containing the body of the rule. A higher confidence value means the rule is more reliable. Mining settings can include a minimum confidence threshold to produce more significant results and limit the number of generated rules.

  • The lift of an association rule indicates its significance by comparing the actual confidence of the rule to its expected confidence, which is calculated as the support of the head divided by the product of the body and head supports. A lift value greater than 1 means the body positively affects the head, a value less than 1 means a negative effect, and a value near 1 means no effect.

Analysis of the obtained rules - basic parameters

Apriori is an algorithm for discovering frequent item sets and association rules in databases. It starts with individual items with high frequency and builds larger sets while maintaining sufficient frequency. The frequent item sets produced by Apriori reveal general trends in the database.

Given the usefulness of the apriori algorithm and its popularity, it is this algorithm that will be used to further analyse association rules.

rules<-apriori(trans, parameter=list(minlen=2))
Apriori

Parameter specification:

Algorithmic control:

Absolute minimum support count: 4884 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[121 item(s), 48842 transaction(s)] done [0.03s].
sorting and recoding items ... [32 item(s)] done [0.01s].
creating transaction tree ... done [0.02s].
checking subsets of size 1 2 3 4 5 6 7 8 9 done [0.09s].
writing ... [7067 rule(s)] done [0.01s].
creating S4 object  ... done [0.02s].
summary(rules)
set of 7067 rules

rule length distribution (lhs + rhs):sizes
   2    3    4    5    6    7    8    9 
 135  718 1724 2245 1559  577  102    7 

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   4.000   5.000   4.927   6.000   9.000 

summary of quality measures:
    support         confidence        coverage           lift            count      
 Min.   :0.1000   Min.   :0.8001   Min.   :0.1003   Min.   :0.8722   Min.   : 4885  
 1st Qu.:0.1150   1st Qu.:0.8891   1st Qu.:0.1245   1st Qu.:1.0001   1st Qu.: 5615  
 Median :0.1360   Median :0.9267   Median :0.1473   Median :1.0278   Median : 6641  
 Mean   :0.1692   Mean   :0.9244   Mean   :0.1839   Mean   :1.2065   Mean   : 8263  
 3rd Qu.:0.1876   3rd Qu.:0.9631   3rd Qu.:0.2052   3rd Qu.:1.1480   3rd Qu.: 9163  
 Max.   :0.8707   Max.   :1.0000   Max.   :0.9533   Max.   :2.7453   Max.   :42525  

mining info:

Basic parameters of the apriori function: support = 0.1 and confidence = 0.8.

rules.supp<-sort(rules, by="support", decreasing=TRUE)
inspectDT(rules.supp[is.significant(rules.supp, trans, alpha=0.05)])

By sorting the resulting rules by the highest support value, we obtain similar conclusions to those reached by frequency chart analysis. The expected combinations race=White and native.country=United-States, or in combination with capital.loss=0 have the highest support value. This means that these connections have the highest number of rules obtained. It is also worth mentioning that only rules that are statistically significant at the 5% significance level were selected using the is.significant function.

rules.conf<-sort(rules, by="confidence", decreasing=TRUE) 
inspectDT(rules.conf[is.significant(rules.conf, trans, alpha=0.05)])

Sorting by highest confidence values already yields slightly different conclusions. A value of one for relationship=Husband and gender=Male should not come as a surprise. This means that when a person has the value relationship=Husband he is also male. There is a similar relationship between relationship=Husband and marital.status=Married for obvious reasons. However, there are many other less obvious and interesting connections that are worth exploring.

rules.lift<-sort(rules, by="lift", decreasing=TRUE)
inspectDT(rules.lift[is.significant(rules.lift, trans, alpha=0.05)])

As described earlier, lift values greater than 1 indicate a positive effect of itemset 𝑋 (body of the rule) on 𝑌 (head of the rule). The highest lift values for the analysed dataset are reached by rules describing the impact of, for example, relationship=Own-child on marital.status=Never-married. Or another obvious example like the influence of marital.status=Married-civ-spouse and gender=Male on {relationship=Husband}.

Digging the rules - examples of interesting relationships

A properly prepared dataset, together with the association rules detection techniques used, provides a wealth of knowledge about the units under investigation. Thanks to a large number of variables with multiple levels, we are able to investigate the most diverse connections between specific values. It would be impossible to prepare and describe all the interesting connections, hence I limited the selection to the study of rules related to income=>50K and native.country=Poland.

RHS

I was first interested in the variables and their values that had the greatest impact on income=>50K.

rules.income<-apriori(data=trans, parameter=list(supp=0.1,conf = 0.4), appearance=list(default="lhs", rhs="income=>50K"), control=list(verbose=F)) 
rules.income.byconf<-sort(rules.income, by="confidence", decreasing=TRUE)
inspectDT(rules.income.byconf)

By placing income=>50K to the right of the rules and adjusting the parameters accordingly, it was possible to obtain 102 rules with a minimum confidence of 0.4 and support = 0.1. For the data analysed, it can be concluded that married white US citizens have the highest probability of high earnings. This rule of thumb reaches a confidence level of 0.464. A good indicator may also be the value of the variable relationship=Husband because it also reaches similar confidence scores. In other rules, variables such as hours.per.week=Working_long-hours and capital.gain=0_capital_gain also appear, which seems reasonable.

LHS

The second interesting idea from my perspective to explore was to check the rules determining the impact of the native.country=Poland variable on all the others.

rules.Poland<-apriori(data=trans, parameter=list(supp=0.00001, conf = 0.1, minlen = 2), 
appearance=list(default="rhs", lhs="native.country=Poland"), control=list(verbose=F)) 
rules.Poland.byconf<-sort(rules.Poland, by="confidence", decreasing=TRUE)
inspectDT(rules.Poland.byconf)

Due to the very small number of people whose native country was Poland, support had to be reduced to as low as 0.00001 in order to comply with the 26 rules. Again, the rules were sorted due to the highest confidence. Unsurprisingly, the highest relationship was achieved between {native.country=Poland} and {race=White}. (the vast majority of Poles are White), {capital.loss=0_capital_loss, capital.gain=0_capital_gain} (significant advantage of these categories over others for the entire dataset). A very high confidence level because 0.805 was also achieved with income=<=50K. This shows that the average Pole surveyed was very likely to have low earnings. Further high values can also be read for rules where there is workclass=Private, gender=Male, hours.per.week=Working_full-time. Thus, it can be concluded that among the surveyed Poles there was a significant preponderance of men who worked privately on a full-time basis. Married people also prevailed among the surveyed Poles. They are also relatively young people (a preponderance of ages 35-44 followed by 25-34) who have graduated from high school.

Visualisation of the results obtained

plot(rules[1:100,], method="matrix", measure="lift")
Itemsets in Antecedent (LHS)
 [1] "{marital.status=Married-civ-spouse}" "{relationship=Husband}"              "{relationship=Own-child}"           
 [4] "{age=17-24}"                         "{income=>50K}"                       "{occupation=Craft-repair}"          
 [7] "{hours.per.week=Working_part-time}"  "{marital.status=Never-married}"      "{marital.status=Divorced}"          
[10] "{hours.per.week=Working_long-hours}" "{relationship=Not-in-family}"        "{gender=Female}"                    
[13] "{education=HS-grad}"                 "{education=Some-college}"            "{relationship=Unmarried}"           
[16] "{occupation=Adm-clerical}"           "{occupation=Sales}"                  "{age=25-34}"                        
[19] "{occupation=Exec-managerial}"        "{age=35-44}"                         "{age=45-54}"                        
[22] "{education=Bachelors}"               "{occupation=Prof-specialty}"        
Itemsets in Consequent (RHS)
[1] "{capital.loss=0_capital_loss}"       "{capital.gain=0_capital_gain}"       "{race=White}"                       
[4] "{native.country=United-States}"      "{income=<=50K}"                      "{gender=Male}"                      
[7] "{marital.status=Married-civ-spouse}" "{relationship=Husband}"              "{marital.status=Never-married}"     

plot(rules, measure=c("support","confidence"), shading="lift")
To reduce overplotting, jitter is added! Use jitter = 0 to prevent jitter.

plot(rules[1:20], method="grouped")

Visualisations for rules with income = >50k

plot(rules.income, measure=c("support","confidence"), shading="lift")

plot(rules.income[1:20,], method="graph")

plot(rules.income, method="paracoord", control=list(reorder=TRUE))

Interactive plotting

set.seed(123)
plot(rules.income, method="graph", measure="support", shading="lift", engine="html")
Warning: Too many rules supplied. Only plotting the best 100 using ‘lift’ (change control parameter max if needed).

The above charts are a valuable addition to the analysis of association rules. Using the example of the rules with income=>50k, you can perfectly see in the graphs the connections to other variables and their values. Graphical analysis provides the same information as a thorough review of the tables of metrics, hence its conclusions are analogous to those described earlier. However, many times graphical analysis provides a better and easier way to see the complexity and strength of connections between variables and their levels.

Summary

As stated earlier association rule learning is a method in machine learning used to uncover relationships between variables in a dataset. The technique involves finding common patterns in elements that occur together and generating rules that suggest connections between elements. The method was used in a study and proved to be effective, providing valuable insights into the connections within the data.

By using the Apriori algorithm, valuable information was obtained regarding the relationship between high income and the other variables and their values, as well as between the native country Poland and the other variables. However, this is a small part of the available rules to explore. In further work on the analysis, it would be worthwhile to deepen the interpretations of the results with other interesting variables like race, age, marital status and education.

LS0tCnRpdGxlOiAiVVNMIHByb2plY3QgLSBBc3NvY2lhdGlvbiBydWxlcyIKYXV0aG9yOiAiQWRhbSBKYW5jenlzenluIgpkYXRlOiAiMjAyMi8yMDIzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogY2VydWxlYW4KICAgIG51bWJlcl9zZWN0aW9uczogbm8KICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGhpZ2hsaWdodDogaGFkZG9jawogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICBkZl9wcmludDogcGFnZWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShza2ltcikKbGlicmFyeShhcnVsZXMpCmxpYnJhcnkoYXJ1bGVzVml6KQpsaWJyYXJ5KGFydWxlc0NCQSkKYGBgCgojIEludHJvZHVjdGlvbgoKIVtTb3VyY2U6CjxodHRwczovL3d3dy5lZHN1cmdlLmNvbS9uZXdzLzIwMjAtMDItMjgtdGhpcy1kaXN0cmljdC1oZWxwcy15b3VuZy1raWRzLWlkZW50aWZ5LXRoZWlyLWludGVyZXN0cy1hbmQtaWRlYWwtY2FyZWVycz5dKGFzc29jaWF0aW9uX3J1bGVzLndlYnApCgo8L2JyPiBBc3NvY2lhdGlvbiBydWxlIGxlYXJuaW5nIGlzIGEgZGF0YSBhbmFseXNpcyB0ZWNobmlxdWUgaW4gbWFjaGluZSBsZWFybmluZyB0aGF0IHJldmVhbHMgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBpbiBhIGxhcmdlIGRhdGFzZXQuIFRoZSBiYXNpYyBpZGVhIGlzIHRvIGZpbmQgY29tbW9uIHBhdHRlcm5zIG9mIGVsZW1lbnRzIHRoYXQgb2NjdXIgdG9nZXRoZXIgaW4gdHJhbnNhY3Rpb25zIG9yIHJlY29yZHMuIFRoZXNlIHBhdHRlcm5zIGFyZSB0aGVuIHVzZWQgdG8gZ2VuZXJhdGUgcnVsZXMsIGNhbGxlZCBhc3NvY2lhdGlvbiBydWxlcywgdGhhdCBzdWdnZXN0IGEgcmVsYXRpb25zaGlwIGJldHdlZW4gZWxlbWVudHMuIFRoZSB1c2Ugb2YgYXNzb2NpYXRpb24gcnVsZSBsZWFybmluZyBpcyBzaWduaWZpY2FudCBpbiBkYXRhIG1pbmluZyBhcyBpdCBwcm92aWRlcyBpbnNpZ2h0cyBpbnRvIHRoZSBjb25uZWN0aW9ucyBiZXR3ZWVuIHZhcmlhYmxlcyBpbiB0aGUgZGF0YS4gVGhpcyBpbmZvcm1hdGlvbiBjYW4gdGhlbiBiZSB1c2VkIHRvIG1ha2UgaW5mb3JtZWQgY2hvaWNlcyBhbmQgZm9yZWNhc3QgZnV0dXJlIHRyZW5kcy4gQnkgZGlzY292ZXJpbmcgaGlkZGVuIHBhdHRlcm5zIGluIHRoZSBkYXRhLCBvcmdhbml6YXRpb25zIGNhbiBhbHNvIGltcHJvdmUgdGhlaXIgZWZmaWNpZW5jeSBhbmQgbWFrZSBtb3JlIGluZm9ybWVkIGRlY2lzaW9ucy4KClRoZSBmb2xsb3dpbmcgYW5hbHlzaXMgaXMgYW4gZXhhbXBsZSBvZiB0aGUgdXNlIG9mIGFzc29jaWF0aW9uIHJ1bGVzIGluCnByYWN0aWNlLiA8L2JyPjwvYnI+CgojIEFib3V0IERhdGFzZXQKCjwvYnI+IFRoZSBkYXRhIHVzZWQgaW4gdGhlIGFuYWx5c2lzIHdhcyB0YWtlbiBmcm9tIEthZ2dsZToKPGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvd2VucnVsaXUvYWR1bHQtaW5jb21lLWRhdGFzZXQ+CgpUaGUgb3JpZ2luYWwgZGF0YXNldCBuYW1lZCAnYWR1bHQnIGNhbWUgZnJvbSB0aGUgVUNJIG1hY2hpbmUgbGVhcm5pbmcKcmVwb3NpdG9yeTogPGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9kYXRhc2V0cy9hZHVsdD4KCkJlZm9yZSBjbGVhbmluZywgdGhlIGRhdGEgY29udGFpbnMgMTUgdmFyaWFibGVzICg2IGNvbnRpbnVvdXMgYW5kIDkgbm9taW5hbC9vcmRpbmFsKSBhbmQgNDg4NDIgb2JzZXJ2YXRpb25zLiBUaGUgZGF0YWJhc2UgY29udGFpbnMgaW5mb3JtYXRpb24gb24gdGhlIGluZGl2aWR1YWxzIGluY29tZSBhbmQgcGVyc29uYWwgaW5mb3JtYXRpb24uIFRoZXNlIGluY2x1ZGUgYWdlLCBnZW5kZXIsIHJhY2UsIGNvdW50cnkgb2Ygb3JpZ2luLCBtYXJpdGFsIHN0YXR1cyBhbmQgb3RoZXJzLiBBbGwgdmFyaWFibGVzIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMgY2FuIGJlIGZvdW5kIGluIHRoZSB0YWJsZXMgYmVsb3cuIDwvYnI+PC9icj4KCi0gICAqKipDb250aW51b3VzIGF0dHJpYnV0ZXMqKioKCnwgKipWYXJpYWJsZSoqICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8Oi0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBhZ2UgICAgICAgICAgICB8IGFnZSBvZiByZXNwb25kZW50ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgZm5sd2d0ICAgICAgICAgfCByZXByZXNlbnRzIGZpbmFsIHdlaWdodCwgd2hpY2ggaXMgdGhlIG51bWJlciBvZiB1bml0cyBpbiB0aGUgdGFyZ2V0IHBvcHVsYXRpb24gdGhhdCB0aGUgcmVzcG9uZGluZyB1bml0IHJlcHJlc2VudHMgfAp8IGVkdWNhdGlvbi5udW0gIHwgbnVtYmVyIG9mIHllYXJzIG9mIGVkdWNhdGlvbiBpbiB0b3RhbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBjYXBpdGFsLmdhaW4gICB8IGluY29tZSBmcm9tIGludmVzdG1lbnQgc291cmNlcyBvdGhlciB0aGFuIHdhZ2Uvc2FsYXJ5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgY2FwaXRhbC5sb3NzICAgfCBsb3NzZXMgZnJvbSBpbnZlc3RtZW50IHNvdXJjZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGhvdXJzLnBlci53ZWVrIHwgbnVtYmVyIG9mIGhvdXJzIHdvcmtlZCBwZXIgd2VlayAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKCjwvYnI+CgotICAgKioqTm9taW5hbC9vcmRpbmFsIGF0dHJpYnV0ZXMqKioKCnwgKipWYXJpYWJsZSoqICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCB3b3JrY2xhc3MgICAgICB8IFByaXZhdGUsIFNlbGYtZW1wLW5vdC1pbmMsIFNlbGYtZW1wLWluYywgRmVkZXJhbC1nb3YsIExvY2FsLWdvdiwgU3RhdGUtZ292LCBXaXRob3V0LXBheSwgTmV2ZXItd29ya2VkICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgZWR1Y2F0aW9uICAgICAgfCBCYWNoZWxvcnMsIFNvbWUtY29sbGVnZSwgMTF0aCwgSFMtZ3JhZCwgUHJvZi1zY2hvb2wsIEFzc29jLWFjZG0sIEFzc29jLXZvYywgOXRoLCA3dGgtOHRoLCAxMnRoLCBNYXN0ZXJzLCAxc3QtNHRoLCAxMHRoLCBEb2N0b3JhdGUsIDV0aC02dGgsIFByZXNjaG9vbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IG1hcml0YWwuc3RhdHVzIHwgTWFycmllZC1jaXYtc3BvdXNlLCBEaXZvcmNlZCwgTmV2ZXItbWFycmllZCwgU2VwYXJhdGVkLCBXaWRvd2VkLCBNYXJyaWVkLXNwb3VzZS1hYnNlbnQsIE1hcnJpZWQtQUYtc3BvdXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBvY2N1cGF0aW9uICAgICB8IFRlY2gtc3VwcG9ydCwgQ3JhZnQtcmVwYWlyLCBPdGhlci1zZXJ2aWNlLCBTYWxlcywgRXhlYy1tYW5hZ2VyaWFsLCBQcm9mLXNwZWNpYWx0eSwgSGFuZGxlcnMtY2xlYW5lcnMsIE1hY2hpbmUtb3AtaW5zcGN0LCBBZG0tY2xlcmljYWwsIEZhcm1pbmctZmlzaGluZywgVHJhbnNwb3J0LW1vdmluZywgUHJpdi1ob3VzZS1zZXJ2LCBQcm90ZWN0aXZlLXNlcnYsIEFybWVkLUZvcmNlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgcmVsYXRpb25zaGlwICAgfCBXaWZlLCBPd24tY2hpbGQsIEh1c2JhbmQsIE5vdC1pbi1mYW1pbHksIE90aGVyLXJlbGF0aXZlLCBVbm1hcnJpZWQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHJhY2UgICAgICAgICAgIHwgV2hpdGUsIEFzaWFuLVBhYy1Jc2xhbmRlciwgQW1lci1JbmRpYW4tRXNraW1vLCBPdGhlciwgQmxhY2sgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBnZW5kZXIgICAgICAgICB8IEZlbWFsZSwgTWFsZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgbmF0aXZlLmNvdW50cnkgfCBVbml0ZWQtU3RhdGVzLCBDYW1ib2RpYSwgRW5nbGFuZCwgUHVlcnRvLVJpY28sIENhbmFkYSwgR2VybWFueSwgT3V0bHlpbmctVVMoR3VhbS1VU1ZJLWV0YyksIEluZGlhLCBKYXBhbiwgR3JlZWNlLCBTb3V0aCwgQ2hpbmEsIEN1YmEsIElyYW4sIEhvbmR1cmFzLCBQaGlsaXBwaW5lcywgSXRhbHksIFBvbGFuZCwgSmFtYWljYSwgVmlldG5hbSwgTWV4aWNvLCBQb3J0dWdhbCwgSXJlbGFuZCwgRnJhbmNlLCBEb21pbmljYW4tUmVwdWJsaWMsIExhb3MsIEVjdWFkb3IsIFRhaXdhbiwgSGFpdGksIENvbHVtYmlhLCBIdW5nYXJ5LCBHdWF0ZW1hbGEsIE5pY2FyYWd1YSwgU2NvdGxhbmQsIFRoYWlsYW5kLCBZdWdvc2xhdmlhLCBFbC1TYWx2YWRvciwgVHJpbmFkYWQmVG9iYWdvLCBQZXJ1LCBIb25nLCBIb2xhbmQtTmV0aGVybGFuZHMgfAp8IGluY29tZSAgICAgICAgIHwgXD41MEssIFw8PTUwSyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKCiMjIExvYWRpbmcgZGF0YQoKYGBge3J9CmRhdGE8LXJlYWQuY3N2KCJhZHVsdC5jc3YiLCBoZWFkZXI9VFJVRSwgc2VwPSIsIikKaGVhZChkYXRhKQpgYGAKCiMjIERhdGEgY2xlYW5pbmcgYW5kIHByZXByb2Nlc3NpbmcKCkluIG9yZGVyIHRvIGFwcGx5IGFzc29jaWF0aW9uIHJ1bGVzIG1ldGhvZHMsIHZhcmlhYmxlcyBuZWVkIHRvIGJlIHByZXBhcmVkLiBUaGUgZGF0YSBtdXN0IGJlIGluIHRyYW5zYWN0aW9uYWwgZm9ybWF0LiBJdCBpcyB0aGVyZWZvcmUgbmVjZXNzYXJ5IHRvIGNvbnZlcnQgY29udGludW91cyB2YXJpYWJsZXMgaW50byBub21pbmFsIHZhcmlhYmxlcy4gV2UgY2FuIGdldCByaWQgb2Ygc29tZSB2YXJpYWJsZXMgc3VjaCBhcyAiZm5sd2d0IiwgImVkdWNhdGlvbmFsLm51bSIsIGFzIHRoZXkgZG8gbm90IHByb3ZpZGUgdmFsdWFibGUgaW5mb3JtYXRpb24gZm9yIGFuYWx5c2lzLiBUaGUgcmVtYWluaW5nIGNvbnRpbnVvdXMgdmFyaWFibGVzIG5lZWQgdG8gYmUgZ3JvdXBlZCBhbmQgY29udmVydGVkIGludG8gYSBmYWN0b3IgdmFyaWFibGUgdHlwZS4KCmBgYHtyfQpkYXRhIDwtIHNlbGVjdChkYXRhLC1jKCJmbmx3Z3QiLCAiZWR1Y2F0aW9uYWwubnVtIikpCmRhdGFbIWNvbXBsZXRlLmNhc2VzKGRhdGEpLF0KYGBgCgpgYGB7cn0KZGF0YSAlPiUgZ3JvdXBfYnkoZWR1Y2F0aW9uKSAlPiUgdGFsbHkoKQpkYXRhICU+JSBncm91cF9ieSh3b3JrY2xhc3MpICU+JSB0YWxseSgpCmRhdGEgJT4lIGdyb3VwX2J5KG1hcml0YWwuc3RhdHVzKSAlPiUgdGFsbHkoKQpkYXRhICU+JSBncm91cF9ieShvY2N1cGF0aW9uKSAlPiUgdGFsbHkoKQpkYXRhICU+JSBncm91cF9ieShyZWxhdGlvbnNoaXApICU+JSB0YWxseSgpCmRhdGEgJT4lIGdyb3VwX2J5KHJhY2UpICU+JSB0YWxseSgpCmRhdGEgJT4lIGdyb3VwX2J5KGdlbmRlcikgJT4lIHRhbGx5KCkKZGF0YSAlPiUgZ3JvdXBfYnkobmF0aXZlLmNvdW50cnkpICU+JSB0YWxseSgpCmRhdGEgJT4lIGdyb3VwX2J5KGluY29tZSkgJT4lIHRhbGx5KCkKYGBgCgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JSBtdXRhdGUod29ya2NsYXNzID0gaWZlbHNlKHdvcmtjbGFzcyA9PSAiPyIsICJVbmtub3duX3dvcmtjbGFzcyIsIHdvcmtjbGFzcykpCmRhdGEgPC0gZGF0YSAlPiUgbXV0YXRlKG9jY3VwYXRpb24gPSBpZmVsc2Uob2NjdXBhdGlvbiA9PSAiPyIsICJVbmtub3duX29jY3VwYXRpb24iLCBvY2N1cGF0aW9uKSkKZGF0YSA8LSBkYXRhICU+JSBtdXRhdGUobmF0aXZlLmNvdW50cnkgPSBpZmVsc2UobmF0aXZlLmNvdW50cnkgPT0gIj8iLCAiVW5rbm93bl9uYXRpdmUuY291bnRyeSIsIG5hdGl2ZS5jb3VudHJ5KSkKc3RyKGRhdGEpCmBgYAoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoYWdlKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBjb2xvciA9ICIjMDAwMDAwIiwgZmlsbCA9ICIjMDA5OUY4IikgKwogIGdlb21fZGVuc2l0eShjb2xvciA9ICIjMDAwMDAwIiwgZmlsbCA9ICIjRjg1NzAwIiwgYWxwaGEgPSAwLjYpCm1heChkYXRhJGFnZSkKbWluKGRhdGEkYWdlKQoKCmRhdGEkYWdlIDwtIGN1dChkYXRhJGFnZSwKICAgICAgICAgICAgYnJlYWtzID0gYygwLDI0LDM0LDQ0LDU0LDY0LDc0LDEwMCksCiAgICAgICAgICAgIGxhYmVscyA9IGMoIjE3LTI0IiwiMjUtMzQiLCIzNS00NCIsIjQ1LTU0IiwiNTUtNjQiLCI2NS03NCIsIjc1KyIpLAogICAgICAgICAgICBvcmRlcmVkX3Jlc3VsdCA9IFQpCgpjbGFzcyhkYXRhJGFnZSkKZGF0YSAlPiUgZ3JvdXBfYnkoYWdlKSAlPiUgdGFsbHkoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSwgYWVzKGhvdXJzLnBlci53ZWVrKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBjb2xvciA9ICIjMDAwMDAwIiwgZmlsbCA9ICIjMDA5OUY4IikgKwogIGdlb21fZGVuc2l0eShjb2xvciA9ICIjMDAwMDAwIiwgZmlsbCA9ICIjRjg1NzAwIiwgYWxwaGEgPSAwLjYpCm1heChkYXRhJGhvdXJzLnBlci53ZWVrKQptaW4oZGF0YSRob3Vycy5wZXIud2VlaykKdGFibGUoZGF0YSRob3Vycy5wZXIud2VlaykKCmRhdGEkaG91cnMucGVyLndlZWsgPC0gY3V0KGRhdGEkaG91cnMucGVyLndlZWssCiAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsMzEsNDAsNjAsMTAwKSwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIldvcmtpbmdfcGFydC10aW1lIiwiV29ya2luZ19mdWxsLXRpbWUiLCJXb3JraW5nX2xvbmctaG91cnMiLCJQb3NzaWJsZV93b3JrYWhvbGljIiksCiAgICAgICAgICAgICAgICBvcmRlcmVkX3Jlc3VsdCA9IFQpCgpjbGFzcyhkYXRhJGhvdXJzLnBlci53ZWVrKQpkYXRhICU+JSBncm91cF9ieShob3Vycy5wZXIud2VlaykgJT4lIHRhbGx5KCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEsIGFlcyhjYXBpdGFsLmdhaW4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGNvbG9yID0gIiMwMDAwMDAiLCBmaWxsID0gIiMwMDk5RjgiKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIiMwMDAwMDAiLCBmaWxsID0gIiNGODU3MDAiLCBhbHBoYSA9IDAuNikKbWF4KGRhdGEkY2FwaXRhbC5nYWluKQptaW4oZGF0YSRjYXBpdGFsLmdhaW4pCnRhYmxlKGRhdGEkY2FwaXRhbC5nYWluKQpgYGAKCmBgYHtyfQp4IDwtIGRhdGEgJT4lIGZpbHRlcihjYXBpdGFsLmdhaW4gPiAwKQoKZ2dwbG90KHgsIGFlcyhjYXBpdGFsLmdhaW4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGNvbG9yID0gIiMwMDAwMDAiLCBmaWxsID0gIiMwMDk5RjgiKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIiMwMDAwMDAiLCBmaWxsID0gIiNGODU3MDAiLCBhbHBoYSA9IDAuNikKbWF4KHgkY2FwaXRhbC5nYWluKQptaW4oeCRjYXBpdGFsLmdhaW4pCnRhYmxlKHgkY2FwaXRhbC5nYWluKQpgYGAKCmBgYHtyfQpkYXRhJGNhcGl0YWwuZ2FpbiA8LSBjdXQoZGF0YSRjYXBpdGFsLmdhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoLTEsMCxtZWRpYW4oeCRjYXBpdGFsLmdhaW4pLG1heCh4JGNhcGl0YWwuZ2FpbikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwX2NhcGl0YWxfZ2FpbiIsImJlbG93X21lZGlhbl9jYXBpdGFsX2dhaW4iLCJhYm92ZV9tZWRpYW5fY2FwaXRhbF9nYWluIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWRfcmVzdWx0ID0gVCkKCmNsYXNzKGRhdGEkY2FwaXRhbC5nYWluKQpkYXRhICU+JSBncm91cF9ieShjYXBpdGFsLmdhaW4pICU+JSB0YWxseSgpCmBgYAoKYGBge3J9CnkgPC0gZGF0YSAlPiUgZmlsdGVyKGNhcGl0YWwubG9zcyA+IDApCgpnZ3Bsb3QoZGF0YSwgYWVzKGNhcGl0YWwubG9zcykpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgY29sb3IgPSAiIzAwMDAwMCIsIGZpbGwgPSAiIzAwOTlGOCIpICsKICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiIzAwMDAwMCIsIGZpbGwgPSAiI0Y4NTcwMCIsIGFscGhhID0gMC42KQptYXgoZGF0YSRjYXBpdGFsLmxvc3MpCm1pbihkYXRhJGNhcGl0YWwubG9zcykKdGFibGUoZGF0YSRjYXBpdGFsLmxvc3MpCmBgYAoKYGBge3J9CmRhdGEkY2FwaXRhbC5sb3NzIDwtIGN1dChkYXRhJGNhcGl0YWwubG9zcywKICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoLTEsMCxtZWRpYW4oeSRjYXBpdGFsLmxvc3MpLG1heCh5JGNhcGl0YWwubG9zcykpLAogICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMF9jYXBpdGFsX2xvc3MiLCJiZWxvd19tZWRpYW5fY2FwaXRhbF9sb3NzIiwiYWJvdmVfbWVkaWFuX2NhcGl0YWxfbG9zcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZF9yZXN1bHQgPSBUKQoKY2xhc3MoZGF0YSRjYXBpdGFsLmxvc3MpCmRhdGEgJT4lIGdyb3VwX2J5KGNhcGl0YWwubG9zcykgJT4lIHRhbGx5KCkKYGBgCgpEdWUgdG8gdGhlIGFzeW1tZXRyaWMgZGlzdHJpYnV0aW9uIG9mIHRoZSB2YXJpYWJsZSBjYXBpdGFsLmdhaW4gYW5kIGNhcGl0YWwubG9zcywgdGhlIG1lZGlhbiBpbnN0ZWFkIG9mIHRoZSBhdmVyYWdlIG9mIGFsbCBub24temVybyBpbmNvbWUvbG9zc2VzIHJlc3BlY3RpdmVseSB3YXMgdXNlZCBpbiBkZXRlcm1pbmluZyB0aGUgbmV3IGxldmVscy4KCmBgYHtyfQpzdW1tYXJ5KGRhdGEpCmBgYAoKYGBge3J9CnRyYW5zIDwtIHRyYW5zYWN0aW9ucyhkYXRhKQpsZW5ndGgodHJhbnMpCkxJU1QoaGVhZCh0cmFucywgMikpCmBgYAoKVGhlIGZpbmlzaGVkIGRhdGEgcHJlcGFyZWQgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgd2FzIHNhdmVkIHVuZGVyIHRoZSBuYW1lICd0cmFucycuIFRoZSBudW1iZXIgb2YgdHJhbnNhY3Rpb25zIGlzIDQ4ODQyLCB3aXRoIGEgZml4ZWQgbGVuZ3RoIG9mIDEzLgoKIyBBbmFseXNpcwoKIyMgSXRlbSBmcmVxdWVuY3kKCmBgYHtyfQpoZWFkKHNvcnQoaXRlbUZyZXF1ZW5jeSh0cmFucywgdHlwZT0iYWJzb2x1dGUiKSwgZGVjcmVhc2luZyA9IFQpLCAyMCkKaGVhZChzb3J0KGl0ZW1GcmVxdWVuY3kodHJhbnMsIHR5cGU9ImFic29sdXRlIiksIGRlY3JlYXNpbmcgPSBGKSwgMjApCml0ZW1GcmVxdWVuY3lQbG90KHRyYW5zLCB0b3BOPTIwLCB0eXBlPSJyZWxhdGl2ZSIsIG1haW49IkZyZXF1ZW5jeSBwbG90IikKYGBgClRoZSBjaGFydCBhYm92ZSBzaG93cyB0aGUgbW9zdCBmcmVxdWVudGx5IG9jY3VycmluZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQuIEFzIHlvdSBjYW4gc2VlLCB0aGVyZSBhcmUgc2V2ZXJhbCB2YWx1ZXMgdGhhdCBhcHBlYXIgaW4gYWxtb3N0IGV2ZXJ5ICd0cmFuc2FjdGlvbicuIEZvciBleGFtcGxlLCB0aGUgZnJlcXVlbmN5IG9mIGNhcGl0YWwubG9zcz0wIGlzIG92ZXIgOTAlLiBUaGlzIHNob3VsZCBub3QgY29tZSBhcyBhIHN1cnByaXNlLCBhcyBpdCB3YXMgYWxyZWFkeSB2aXNpYmxlIGluIHRoZSBkYXRhIGFuYWx5c2lzLiBUaGUgc2FtZSBpcyB0cnVlIGZvciB0aGUgdmFsdWVzIG5hdGl2ZS5jb3VudHJ5PVVuaXRlZC1TdGF0ZXMgYW5kIHJhY2U9V2hpdGUuIEFnYWluLCB0aGlzIGlzIG5vdCBzdXJwcmlzaW5nIGFzIHRoZSBkYXRhIHJlbGF0ZXMgdG8gcGVvcGxlIGZyb20gdGhlIFVuaXRlZCBTdGF0ZXMsIHdoZXJlIHdoaXRlIHBlb3BsZSBtYWRlIHVwIHRoZSB2YXN0IG1ham9yaXR5IG9mIHRoZSBwb3B1bGF0aW9uIGF0IHRoZSB0aW1lIG9mIGRhdGEgY29sbGVjdGlvbi4KCkl0IGlzIHRoZXJlZm9yZSB0byBiZSBleHBlY3RlZCB0aGF0IHRoZXNlIG1vc3QgY29tbW9uIHZhbHVlcyB3aWxsIGFwcGVhciB3aXRoIHRoZSB2YXN0IG1ham9yaXR5IG9mIHJ1bGVzLgoKIyMgTWVhc3VyaW5nIHJ1bGUgaW50ZXJlc3QKCi0gU3VwcG9ydCBvZiBhbiBhc3NvY2lhdGlvbiBydWxlIGlzIHRoZSBwZXJjZW50YWdlIG9mIGdyb3VwcyBjb250YWluaW5nIGFsbCBlbGVtZW50cyBpbiB0aGUgcnVsZSwgY2FsY3VsYXRlZCBmcm9tIHRoZSB0b3RhbCBudW1iZXIgb2YgZ3JvdXBzIGV4YW1pbmVkLiBJdCByZXByZXNlbnRzIHRoZSBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZSBvZiB0aGUgcnVsZSdzIGJvZHkgYW5kIGhlYWQuIFNldHRpbmcgYSBtaW5pbXVtIHN1cHBvcnQgbGV2ZWwgd2hlbiBtaW5pbmcgY2FuIHByb2R1Y2UgbW9yZSBzaWduaWZpY2FudCByZXN1bHRzIGFuZCBjb250cm9sIHRoZSBudW1iZXIgb2YgcnVsZXMgZ2VuZXJhdGVkLgoKLSBDb25maWRlbmNlIG1lYXN1cmVzIHRoZSByZWxpYWJpbGl0eSBvZiBhbiBhc3NvY2lhdGlvbiBydWxlIGJ5IGluZGljYXRpbmcgdGhlIGZyZXF1ZW5jeSBhdCB3aGljaCB0aGUgaGVhZCBvZiB0aGUgcnVsZSBhcHBlYXJzIGluIGdyb3VwcyBjb250YWluaW5nIHRoZSBib2R5IG9mIHRoZSBydWxlLiBBIGhpZ2hlciBjb25maWRlbmNlIHZhbHVlIG1lYW5zIHRoZSBydWxlIGlzIG1vcmUgcmVsaWFibGUuIE1pbmluZyBzZXR0aW5ncyBjYW4gaW5jbHVkZSBhIG1pbmltdW0gY29uZmlkZW5jZSB0aHJlc2hvbGQgdG8gcHJvZHVjZSBtb3JlIHNpZ25pZmljYW50IHJlc3VsdHMgYW5kIGxpbWl0IHRoZSBudW1iZXIgb2YgZ2VuZXJhdGVkIHJ1bGVzLgoKLSBUaGUgbGlmdCBvZiBhbiBhc3NvY2lhdGlvbiBydWxlIGluZGljYXRlcyBpdHMgc2lnbmlmaWNhbmNlIGJ5IGNvbXBhcmluZyB0aGUgYWN0dWFsIGNvbmZpZGVuY2Ugb2YgdGhlIHJ1bGUgdG8gaXRzIGV4cGVjdGVkIGNvbmZpZGVuY2UsIHdoaWNoIGlzIGNhbGN1bGF0ZWQgYXMgdGhlIHN1cHBvcnQgb2YgdGhlIGhlYWQgZGl2aWRlZCBieSB0aGUgcHJvZHVjdCBvZiB0aGUgYm9keSBhbmQgaGVhZCBzdXBwb3J0cy4gQSBsaWZ0IHZhbHVlIGdyZWF0ZXIgdGhhbiAxIG1lYW5zIHRoZSBib2R5IHBvc2l0aXZlbHkgYWZmZWN0cyB0aGUgaGVhZCwgYSB2YWx1ZSBsZXNzIHRoYW4gMSBtZWFucyBhIG5lZ2F0aXZlIGVmZmVjdCwgYW5kIGEgdmFsdWUgbmVhciAxIG1lYW5zIG5vIGVmZmVjdC4KCiMjIEFuYWx5c2lzIG9mIHRoZSBvYnRhaW5lZCBydWxlcyAtIGJhc2ljIHBhcmFtZXRlcnMKCkFwcmlvcmkgaXMgYW4gYWxnb3JpdGhtIGZvciBkaXNjb3ZlcmluZyBmcmVxdWVudCBpdGVtIHNldHMgYW5kIGFzc29jaWF0aW9uIHJ1bGVzIGluIGRhdGFiYXNlcy4gSXQgc3RhcnRzIHdpdGggaW5kaXZpZHVhbCBpdGVtcyB3aXRoIGhpZ2ggZnJlcXVlbmN5IGFuZCBidWlsZHMgbGFyZ2VyIHNldHMgd2hpbGUgbWFpbnRhaW5pbmcgc3VmZmljaWVudCBmcmVxdWVuY3kuIFRoZSBmcmVxdWVudCBpdGVtIHNldHMgcHJvZHVjZWQgYnkgQXByaW9yaSByZXZlYWwgZ2VuZXJhbCB0cmVuZHMgaW4gdGhlIGRhdGFiYXNlLgoKR2l2ZW4gdGhlIHVzZWZ1bG5lc3Mgb2YgdGhlIGFwcmlvcmkgYWxnb3JpdGhtIGFuZCBpdHMgcG9wdWxhcml0eSwgaXQgaXMgdGhpcyBhbGdvcml0aG0gdGhhdCB3aWxsIGJlIHVzZWQgdG8gZnVydGhlciBhbmFseXNlIGFzc29jaWF0aW9uIHJ1bGVzLgoKYGBge3J9CnJ1bGVzPC1hcHJpb3JpKHRyYW5zLCBwYXJhbWV0ZXI9bGlzdChtaW5sZW49MikpCnN1bW1hcnkocnVsZXMpCmBgYAoKQmFzaWMgcGFyYW1ldGVycyBvZiB0aGUgYXByaW9yaSBmdW5jdGlvbjogc3VwcG9ydCA9IDAuMSBhbmQgY29uZmlkZW5jZSA9IDAuOC4KCmBgYHtyfQpydWxlcy5zdXBwPC1zb3J0KHJ1bGVzLCBieT0ic3VwcG9ydCIsIGRlY3JlYXNpbmc9VFJVRSkKaW5zcGVjdERUKHJ1bGVzLnN1cHBbaXMuc2lnbmlmaWNhbnQocnVsZXMuc3VwcCwgdHJhbnMsIGFscGhhPTAuMDUpXSkKYGBgCgpCeSBzb3J0aW5nIHRoZSByZXN1bHRpbmcgcnVsZXMgYnkgdGhlIGhpZ2hlc3Qgc3VwcG9ydCB2YWx1ZSwgd2Ugb2J0YWluIHNpbWlsYXIgY29uY2x1c2lvbnMgdG8gdGhvc2UgcmVhY2hlZCBieSBmcmVxdWVuY3kgY2hhcnQgYW5hbHlzaXMuIFRoZSBleHBlY3RlZCBjb21iaW5hdGlvbnMgcmFjZT1XaGl0ZSBhbmQgbmF0aXZlLmNvdW50cnk9VW5pdGVkLVN0YXRlcywgb3IgaW4gY29tYmluYXRpb24gd2l0aCBjYXBpdGFsLmxvc3M9MCBoYXZlIHRoZSBoaWdoZXN0IHN1cHBvcnQgdmFsdWUuIFRoaXMgbWVhbnMgdGhhdCB0aGVzZSBjb25uZWN0aW9ucyBoYXZlIHRoZSBoaWdoZXN0IG51bWJlciBvZiBydWxlcyBvYnRhaW5lZC4gSXQgaXMgYWxzbyB3b3J0aCBtZW50aW9uaW5nIHRoYXQgb25seSBydWxlcyB0aGF0IGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IHRoZSA1JSBzaWduaWZpY2FuY2UgbGV2ZWwgd2VyZSBzZWxlY3RlZCB1c2luZyB0aGUgaXMuc2lnbmlmaWNhbnQgZnVuY3Rpb24uCgpgYGB7cn0KcnVsZXMuY29uZjwtc29ydChydWxlcywgYnk9ImNvbmZpZGVuY2UiLCBkZWNyZWFzaW5nPVRSVUUpIAppbnNwZWN0RFQocnVsZXMuY29uZltpcy5zaWduaWZpY2FudChydWxlcy5jb25mLCB0cmFucywgYWxwaGE9MC4wNSldKQpgYGAKClNvcnRpbmcgYnkgaGlnaGVzdCBjb25maWRlbmNlIHZhbHVlcyBhbHJlYWR5IHlpZWxkcyBzbGlnaHRseSBkaWZmZXJlbnQgY29uY2x1c2lvbnMuIEEgdmFsdWUgb2Ygb25lIGZvciByZWxhdGlvbnNoaXA9SHVzYmFuZCBhbmQgZ2VuZGVyPU1hbGUgc2hvdWxkIG5vdCBjb21lIGFzIGEgc3VycHJpc2UuIFRoaXMgbWVhbnMgdGhhdCB3aGVuIGEgcGVyc29uIGhhcyB0aGUgdmFsdWUgcmVsYXRpb25zaGlwPUh1c2JhbmQgaGUgaXMgYWxzbyBtYWxlLiBUaGVyZSBpcyBhIHNpbWlsYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gcmVsYXRpb25zaGlwPUh1c2JhbmQgYW5kIG1hcml0YWwuc3RhdHVzPU1hcnJpZWQgZm9yIG9idmlvdXMgcmVhc29ucy4gSG93ZXZlciwgdGhlcmUgYXJlIG1hbnkgb3RoZXIgbGVzcyBvYnZpb3VzIGFuZCBpbnRlcmVzdGluZyBjb25uZWN0aW9ucyB0aGF0IGFyZSB3b3J0aCBleHBsb3JpbmcuCgpgYGB7cn0KcnVsZXMubGlmdDwtc29ydChydWxlcywgYnk9ImxpZnQiLCBkZWNyZWFzaW5nPVRSVUUpCmluc3BlY3REVChydWxlcy5saWZ0W2lzLnNpZ25pZmljYW50KHJ1bGVzLmxpZnQsIHRyYW5zLCBhbHBoYT0wLjA1KV0pCmBgYAoKQXMgZGVzY3JpYmVkIGVhcmxpZXIsIGxpZnQgdmFsdWVzIGdyZWF0ZXIgdGhhbiAxIGluZGljYXRlIGEgcG9zaXRpdmUgZWZmZWN0IG9mIGl0ZW1zZXQg8J2RiyAoYm9keSBvZiB0aGUgcnVsZSkgb24g8J2RjCAoaGVhZCBvZiB0aGUgcnVsZSkuIFRoZSBoaWdoZXN0IGxpZnQgdmFsdWVzIGZvciB0aGUgYW5hbHlzZWQgZGF0YXNldCBhcmUgcmVhY2hlZCBieSBydWxlcyBkZXNjcmliaW5nIHRoZSBpbXBhY3Qgb2YsIGZvciBleGFtcGxlLCByZWxhdGlvbnNoaXA9T3duLWNoaWxkIG9uIG1hcml0YWwuc3RhdHVzPU5ldmVyLW1hcnJpZWQuIE9yIGFub3RoZXIgb2J2aW91cyBleGFtcGxlIGxpa2UgdGhlIGluZmx1ZW5jZSBvZiBtYXJpdGFsLnN0YXR1cz1NYXJyaWVkLWNpdi1zcG91c2UgYW5kIGdlbmRlcj1NYWxlIG9uIHtyZWxhdGlvbnNoaXA9SHVzYmFuZH0uCgojIERpZ2dpbmcgdGhlIHJ1bGVzIC0gZXhhbXBsZXMgb2YgaW50ZXJlc3RpbmcgcmVsYXRpb25zaGlwcwoKQSBwcm9wZXJseSBwcmVwYXJlZCBkYXRhc2V0LCB0b2dldGhlciB3aXRoIHRoZSBhc3NvY2lhdGlvbiBydWxlcyBkZXRlY3Rpb24gdGVjaG5pcXVlcyB1c2VkLCBwcm92aWRlcyBhIHdlYWx0aCBvZiBrbm93bGVkZ2UgYWJvdXQgdGhlIHVuaXRzIHVuZGVyIGludmVzdGlnYXRpb24uIFRoYW5rcyB0byBhIGxhcmdlIG51bWJlciBvZiB2YXJpYWJsZXMgd2l0aCBtdWx0aXBsZSBsZXZlbHMsIHdlIGFyZSBhYmxlIHRvIGludmVzdGlnYXRlIHRoZSBtb3N0IGRpdmVyc2UgY29ubmVjdGlvbnMgYmV0d2VlbiBzcGVjaWZpYyB2YWx1ZXMuIEl0IHdvdWxkIGJlIGltcG9zc2libGUgdG8gcHJlcGFyZSBhbmQgZGVzY3JpYmUgYWxsIHRoZSBpbnRlcmVzdGluZyBjb25uZWN0aW9ucywgaGVuY2UgSSBsaW1pdGVkIHRoZSBzZWxlY3Rpb24gdG8gdGhlIHN0dWR5IG9mIHJ1bGVzIHJlbGF0ZWQgdG8gaW5jb21lPT41MEsgYW5kIG5hdGl2ZS5jb3VudHJ5PVBvbGFuZC4KCiMjIFJIUwoKSSB3YXMgZmlyc3QgaW50ZXJlc3RlZCBpbiB0aGUgdmFyaWFibGVzIGFuZCB0aGVpciB2YWx1ZXMgdGhhdCBoYWQgdGhlIGdyZWF0ZXN0IGltcGFjdCBvbiBpbmNvbWU9PjUwSy4KCmBgYHtyfQpydWxlcy5pbmNvbWU8LWFwcmlvcmkoZGF0YT10cmFucywgcGFyYW1ldGVyPWxpc3Qoc3VwcD0wLjEsY29uZiA9IDAuNCksIGFwcGVhcmFuY2U9bGlzdChkZWZhdWx0PSJsaHMiLCByaHM9ImluY29tZT0+NTBLIiksIGNvbnRyb2w9bGlzdCh2ZXJib3NlPUYpKSAKcnVsZXMuaW5jb21lLmJ5Y29uZjwtc29ydChydWxlcy5pbmNvbWUsIGJ5PSJjb25maWRlbmNlIiwgZGVjcmVhc2luZz1UUlVFKQppbnNwZWN0RFQocnVsZXMuaW5jb21lLmJ5Y29uZikKYGBgCgpCeSBwbGFjaW5nIGluY29tZT0+NTBLIHRvIHRoZSByaWdodCBvZiB0aGUgcnVsZXMgYW5kIGFkanVzdGluZyB0aGUgcGFyYW1ldGVycyBhY2NvcmRpbmdseSwgaXQgd2FzIHBvc3NpYmxlIHRvIG9idGFpbiAxMDIgcnVsZXMgd2l0aCBhIG1pbmltdW0gY29uZmlkZW5jZSBvZiAwLjQgYW5kIHN1cHBvcnQgPSAwLjEuIEZvciB0aGUgZGF0YSBhbmFseXNlZCwgaXQgY2FuIGJlIGNvbmNsdWRlZCB0aGF0IG1hcnJpZWQgd2hpdGUgVVMgY2l0aXplbnMgaGF2ZSB0aGUgaGlnaGVzdCBwcm9iYWJpbGl0eSBvZiBoaWdoIGVhcm5pbmdzLiBUaGlzIHJ1bGUgb2YgdGh1bWIgcmVhY2hlcyBhIGNvbmZpZGVuY2UgbGV2ZWwgb2YgMC40NjQuIEEgZ29vZCBpbmRpY2F0b3IgbWF5IGFsc28gYmUgdGhlIHZhbHVlIG9mIHRoZSB2YXJpYWJsZSByZWxhdGlvbnNoaXA9SHVzYmFuZCBiZWNhdXNlIGl0IGFsc28gcmVhY2hlcyBzaW1pbGFyIGNvbmZpZGVuY2Ugc2NvcmVzLiBJbiBvdGhlciBydWxlcywgdmFyaWFibGVzIHN1Y2ggYXMgaG91cnMucGVyLndlZWs9V29ya2luZ19sb25nLWhvdXJzIGFuZCBjYXBpdGFsLmdhaW49MF9jYXBpdGFsX2dhaW4gYWxzbyBhcHBlYXIsIHdoaWNoIHNlZW1zIHJlYXNvbmFibGUuICAKCiMjIExIUwoKVGhlIHNlY29uZCBpbnRlcmVzdGluZyBpZGVhIGZyb20gbXkgcGVyc3BlY3RpdmUgdG8gZXhwbG9yZSB3YXMgdG8gY2hlY2sgdGhlIHJ1bGVzIGRldGVybWluaW5nIHRoZSBpbXBhY3Qgb2YgdGhlIG5hdGl2ZS5jb3VudHJ5PVBvbGFuZCB2YXJpYWJsZSBvbiBhbGwgdGhlIG90aGVycy4KCmBgYHtyfQpydWxlcy5Qb2xhbmQ8LWFwcmlvcmkoZGF0YT10cmFucywgcGFyYW1ldGVyPWxpc3Qoc3VwcD0wLjAwMDAxLCBjb25mID0gMC4xLCBtaW5sZW4gPSAyKSwgCmFwcGVhcmFuY2U9bGlzdChkZWZhdWx0PSJyaHMiLCBsaHM9Im5hdGl2ZS5jb3VudHJ5PVBvbGFuZCIpLCBjb250cm9sPWxpc3QodmVyYm9zZT1GKSkgCnJ1bGVzLlBvbGFuZC5ieWNvbmY8LXNvcnQocnVsZXMuUG9sYW5kLCBieT0iY29uZmlkZW5jZSIsIGRlY3JlYXNpbmc9VFJVRSkKaW5zcGVjdERUKHJ1bGVzLlBvbGFuZC5ieWNvbmYpCmBgYAoKRHVlIHRvIHRoZSB2ZXJ5IHNtYWxsIG51bWJlciBvZiBwZW9wbGUgd2hvc2UgbmF0aXZlIGNvdW50cnkgd2FzIFBvbGFuZCwgc3VwcG9ydCBoYWQgdG8gYmUgcmVkdWNlZCB0byBhcyBsb3cgYXMgMC4wMDAwMSBpbiBvcmRlciB0byBjb21wbHkgd2l0aCB0aGUgMjYgcnVsZXMuIEFnYWluLCB0aGUgcnVsZXMgd2VyZSBzb3J0ZWQgZHVlIHRvIHRoZSBoaWdoZXN0IGNvbmZpZGVuY2UuIFVuc3VycHJpc2luZ2x5LCB0aGUgaGlnaGVzdCByZWxhdGlvbnNoaXAgd2FzIGFjaGlldmVkIGJldHdlZW4ge25hdGl2ZS5jb3VudHJ5PVBvbGFuZH0gYW5kIHtyYWNlPVdoaXRlfS4gKHRoZSB2YXN0IG1ham9yaXR5IG9mIFBvbGVzIGFyZSBXaGl0ZSksIHtjYXBpdGFsLmxvc3M9MF9jYXBpdGFsX2xvc3MsIGNhcGl0YWwuZ2Fpbj0wX2NhcGl0YWxfZ2Fpbn0gKHNpZ25pZmljYW50IGFkdmFudGFnZSBvZiB0aGVzZSBjYXRlZ29yaWVzIG92ZXIgb3RoZXJzIGZvciB0aGUgZW50aXJlIGRhdGFzZXQpLiBBIHZlcnkgaGlnaCBjb25maWRlbmNlIGxldmVsIGJlY2F1c2UgMC44MDUgd2FzIGFsc28gYWNoaWV2ZWQgd2l0aCBpbmNvbWU9PD01MEsuIFRoaXMgc2hvd3MgdGhhdCB0aGUgYXZlcmFnZSBQb2xlIHN1cnZleWVkIHdhcyB2ZXJ5IGxpa2VseSB0byBoYXZlIGxvdyBlYXJuaW5ncy4gRnVydGhlciBoaWdoIHZhbHVlcyBjYW4gYWxzbyBiZSByZWFkIGZvciBydWxlcyB3aGVyZSB0aGVyZSBpcyB3b3JrY2xhc3M9UHJpdmF0ZSwgZ2VuZGVyPU1hbGUsIGhvdXJzLnBlci53ZWVrPVdvcmtpbmdfZnVsbC10aW1lLiBUaHVzLCBpdCBjYW4gYmUgY29uY2x1ZGVkIHRoYXQgYW1vbmcgdGhlIHN1cnZleWVkIFBvbGVzIHRoZXJlIHdhcyBhIHNpZ25pZmljYW50IHByZXBvbmRlcmFuY2Ugb2YgbWVuIHdobyB3b3JrZWQgcHJpdmF0ZWx5IG9uIGEgZnVsbC10aW1lIGJhc2lzLiBNYXJyaWVkIHBlb3BsZSBhbHNvIHByZXZhaWxlZCBhbW9uZyB0aGUgc3VydmV5ZWQgUG9sZXMuIFRoZXkgYXJlIGFsc28gcmVsYXRpdmVseSB5b3VuZyBwZW9wbGUgKGEgcHJlcG9uZGVyYW5jZSBvZiBhZ2VzIDM1LTQ0IGZvbGxvd2VkIGJ5IDI1LTM0KSB3aG8gaGF2ZSBncmFkdWF0ZWQgZnJvbSBoaWdoIHNjaG9vbC4KCiMgVmlzdWFsaXNhdGlvbiBvZiB0aGUgcmVzdWx0cyBvYnRhaW5lZAoKYGBge3J9CnBsb3QocnVsZXNbMToxMDAsXSwgbWV0aG9kPSJtYXRyaXgiLCBtZWFzdXJlPSJsaWZ0IikKYGBgCgpgYGB7cn0KcGxvdChydWxlcywgbWVhc3VyZT1jKCJzdXBwb3J0IiwiY29uZmlkZW5jZSIpLCBzaGFkaW5nPSJsaWZ0IikKYGBgCgoKYGBge3J9CnBsb3QocnVsZXNbMToyMF0sIG1ldGhvZD0iZ3JvdXBlZCIpCmBgYAoKIyMgVmlzdWFsaXNhdGlvbnMgZm9yIHJ1bGVzIHdpdGggaW5jb21lID0gPjUwawoKYGBge3J9CnBsb3QocnVsZXMuaW5jb21lLCBtZWFzdXJlPWMoInN1cHBvcnQiLCJjb25maWRlbmNlIiksIHNoYWRpbmc9ImxpZnQiKQpgYGAKCmBgYHtyfQpwbG90KHJ1bGVzLmluY29tZVsxOjIwLF0sIG1ldGhvZD0iZ3JhcGgiKQpgYGAKCgpgYGB7cn0KcGxvdChydWxlcy5pbmNvbWUsIG1ldGhvZD0icGFyYWNvb3JkIiwgY29udHJvbD1saXN0KHJlb3JkZXI9VFJVRSkpCmBgYAoKIyMgSW50ZXJhY3RpdmUgcGxvdHRpbmcKCmBgYHtyfQpzZXQuc2VlZCgxMjMpCnBsb3QocnVsZXMuaW5jb21lLCBtZXRob2Q9ImdyYXBoIiwgbWVhc3VyZT0ic3VwcG9ydCIsIHNoYWRpbmc9ImxpZnQiLCBlbmdpbmU9Imh0bWwiKQpgYGAKClRoZSBhYm92ZSBjaGFydHMgYXJlIGEgdmFsdWFibGUgYWRkaXRpb24gdG8gdGhlIGFuYWx5c2lzIG9mIGFzc29jaWF0aW9uIHJ1bGVzLiBVc2luZyB0aGUgZXhhbXBsZSBvZiB0aGUgcnVsZXMgd2l0aCBpbmNvbWU9PjUwaywgeW91IGNhbiBwZXJmZWN0bHkgc2VlIGluIHRoZSBncmFwaHMgdGhlIGNvbm5lY3Rpb25zIHRvIG90aGVyIHZhcmlhYmxlcyBhbmQgdGhlaXIgdmFsdWVzLiBHcmFwaGljYWwgYW5hbHlzaXMgcHJvdmlkZXMgdGhlIHNhbWUgaW5mb3JtYXRpb24gYXMgYSB0aG9yb3VnaCByZXZpZXcgb2YgdGhlIHRhYmxlcyBvZiBtZXRyaWNzLCBoZW5jZSBpdHMgY29uY2x1c2lvbnMgYXJlIGFuYWxvZ291cyB0byB0aG9zZSBkZXNjcmliZWQgZWFybGllci4gSG93ZXZlciwgbWFueSB0aW1lcyBncmFwaGljYWwgYW5hbHlzaXMgcHJvdmlkZXMgYSBiZXR0ZXIgYW5kIGVhc2llciB3YXkgdG8gc2VlIHRoZSBjb21wbGV4aXR5IGFuZCBzdHJlbmd0aCBvZiBjb25uZWN0aW9ucyBiZXR3ZWVuIHZhcmlhYmxlcyBhbmQgdGhlaXIgbGV2ZWxzLgoKIyBTdW1tYXJ5CgpBcyBzdGF0ZWQgZWFybGllciBhc3NvY2lhdGlvbiBydWxlIGxlYXJuaW5nIGlzIGEgbWV0aG9kIGluIG1hY2hpbmUgbGVhcm5pbmcgdXNlZCB0byB1bmNvdmVyIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgaW4gYSBkYXRhc2V0LiBUaGUgdGVjaG5pcXVlIGludm9sdmVzIGZpbmRpbmcgY29tbW9uIHBhdHRlcm5zIGluIGVsZW1lbnRzIHRoYXQgb2NjdXIgdG9nZXRoZXIgYW5kIGdlbmVyYXRpbmcgcnVsZXMgdGhhdCBzdWdnZXN0IGNvbm5lY3Rpb25zIGJldHdlZW4gZWxlbWVudHMuIFRoZSBtZXRob2Qgd2FzIHVzZWQgaW4gYSBzdHVkeSBhbmQgcHJvdmVkIHRvIGJlIGVmZmVjdGl2ZSwgcHJvdmlkaW5nIHZhbHVhYmxlIGluc2lnaHRzIGludG8gdGhlIGNvbm5lY3Rpb25zIHdpdGhpbiB0aGUgZGF0YS4KCkJ5IHVzaW5nIHRoZSBBcHJpb3JpIGFsZ29yaXRobSwgdmFsdWFibGUgaW5mb3JtYXRpb24gd2FzIG9idGFpbmVkIHJlZ2FyZGluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaGlnaCBpbmNvbWUgYW5kIHRoZSBvdGhlciB2YXJpYWJsZXMgYW5kIHRoZWlyIHZhbHVlcywgYXMgd2VsbCBhcyBiZXR3ZWVuIHRoZSBuYXRpdmUgY291bnRyeSBQb2xhbmQgYW5kIHRoZSBvdGhlciB2YXJpYWJsZXMuIEhvd2V2ZXIsIHRoaXMgaXMgYSBzbWFsbCBwYXJ0IG9mIHRoZSBhdmFpbGFibGUgcnVsZXMgdG8gZXhwbG9yZS4gSW4gZnVydGhlciB3b3JrIG9uIHRoZSBhbmFseXNpcywgaXQgd291bGQgYmUgd29ydGh3aGlsZSB0byBkZWVwZW4gdGhlIGludGVycHJldGF0aW9ucyBvZiB0aGUgcmVzdWx0cyB3aXRoIG90aGVyIGludGVyZXN0aW5nIHZhcmlhYmxlcyBsaWtlIHJhY2UsIGFnZSwgbWFyaXRhbCBzdGF0dXMgYW5kIGVkdWNhdGlvbi4KCiMgUmVmZXJlbmNlcwoKPGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvd2VucnVsaXUvYWR1bHQtaW5jb21lLWRhdGFzZXQ+Cgo8aHR0cHM6Ly93d3cuaWJtLmNvbS9kb2NzL2VuL2lhcz90b3BpYz1wcm9jZWR1cmVzLWFzc29jaWF0aW9uLXJ1bGVzPgo=