Introduction

In this section we will see basic aspects of how to describe a numerical variable. For that, we will continue to use the 2018/19 regional report “The Pulse of Democracy”, available here, where the main findings of the 2018/19 round of the AmericasBarometer are presented. One of the sections of this document reports information on social networks and political attitudes. This section presents information on Internet use and the use of social networks, in general, by country and by certain sociodemographic characteristics.

About the dataset

The data we are going to use should be cited as follows: Source: AmericasBarometer by the Latin American Public Opinion Project (LAPOP), wwww.LapopSurveys.org. This section loads a trimmed database. This database is hosted in the “materials_edu” repository of the LAPOP account on GitHub. It is recommended to clean the Environment before starting this module.

Using the library rio and the command import, you can import this database from this repository. In addition, the data from countries with codes less than or equal to 35 are selected, that is, the observations of the United States and Canada are eliminated.

library(rio)
lapop18 = import("https://raw.github.com/lapop-central/materials_edu/main/LAPOP_AB_Merge_2018_v1.0.sav")
lapop18 = subset(lapop18, pais<=35)

Describing a numeric variable

Table 3.2 of the report “The Pulse of Democracy” shows the general averages of the variables age (“q2” in the database) and years of study (“ed” in the database) for the general population.

The command mean is used to calculate the average and na.rm=T is used because these variables have missing values.

mean(lapop18$q2, na.rm=T)
## [1] 39.99204
mean(lapop18$ed, na.rm=T)
## [1] 9.934748

In the section where we worked with qualitative variables (or factor variables, in the language of R), we saw that the variables “men” and “urban” could be described by defining these variables as a factor, labeling them and making a frequency table of these variables. Other way to find the percentage of people who are men or who live in urban areas is to work with these variables, but not define them as a factor. When variables are created, they are both defined by default as numeric. In this case, in addition to being numeric, they are variables of type dummy, that is, with values 0 and 1. In the case of the “men” variable, we defined 0 = Woman and 1 = Man; and in the case of the “urban” variable we define 0 = Rural and 1 = Urban. It is a good practice to name the dummy variable with a name that refers to category 1. With dummy variables, when the average is calculated, the result is the same as the percentage of category 1. So, if mean(lapop$man, na.rm=T) is calculated, this operation gives us the percentage of category 1, that is, men. It is multiplied by 100 to put it in a format from 0 to 100.

lapop18$men = 2-lapop18$q1
lapop18$urban = 2-lapop18$ur
mean(lapop18$men, na.rm=T)*100
## [1] 49.74846
mean(lapop18$urban, na.rm=T)*100
## [1] 71.15398

These results are the same as those presented in the first column of results for the general population, except for the wealth variable (“quintall”) which is not available in this trimmed version of the dataset.

Plotting a numeric variable

After describing a numeric variable, you can also include some basic plots, for example, using the command hist you can produce the histogram of the variable “years of education” (ed).

hist(lapop18$ed)

This same plot can be reproduced using the command ggplot. With this command we have more flexibility with the graphic options. First, we define the dataframe to be used “lapop18” and the variable “ed” on the X axis. Then we define the specification geom_histogram() to produce a histogram. We define the width of the histogram bar with banwidth=1. Finally, this code allows you to label the X and Y axis and include a black and white theme, with theme_bw().

library(ggplot2)
ggplot(lapop18, aes(x=ed))+
  geom_histogram(binwidth = 1)+
  xlab("Years of education")+
  ylab("Frecuency")+
  theme_bw()

Average by groups

Table 3.2 of the report shows the mean of these numerical variables by groups of variables related to social networks. For example, it shows the average of years of study for Facebook users and for non-Facebook users. If we want to calculate the average of years of study for Facebook users, we first calculate this variable, in the same way as in previous sections, with the command ifelse.

lapop18$fb_user <- ifelse(lapop18$smedia1==1 & lapop18$smedia2<=4, 1, 0)
lapop18$tw_user <- ifelse(lapop18$smedia4==1 & lapop18$smedia5<=4, 1, 0)
lapop18$wa_user <- ifelse(lapop18$smedia7==1 & lapop18$smedia8<=4, 1, 0)

Calculating the average years for Facebook users and non-users can be done in many ways. A first option is by using the square brackets [...]. In this case, we will calculate the average years of study by user groups [lapop18$fb_user==1] and non-Facebook users [lapop18$fb_user==0].

mean(lapop18$ed[lapop18$fb_user==0], na.rm=T)
## [1] 8.064905
mean(lapop18$ed[lapop18$fb_user==1], na.rm=T)
## [1] 11.44839

Describing of a numerical variable by groups

Other way to describe a numeric variable is by using the command summary. This command reports the most used descriptive statistics for a numerical variable: minimum, maximum, quartiles, mean and median. All these statistics allow a better comparison between both groups, users and non-users of Facebook. Within this command you can include the digits=3 specification to round the results, which avoids having to use round, for example.

summary(lapop18$ed[lapop18$fb_user==0], na.rm=T, digits=3)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    0.00    5.00    8.00    8.06   11.00   18.00    1374
summary(lapop18$ed[lapop18$fb_user==1], na.rm=T, digits=3)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##     0.0     9.0    12.0    11.4    14.0    18.0    1240

However, the command summary does not return an important statistic such as the standard deviation, a measure of dispersion or heterogeneity. In order to have the above statistics and include the standard deviation, among other additional measurements, you can use the command describeBy, which is part of the library psych. This command asks for the variable to describe (“ed”) and the variable that forms the groups (“fb_user”) and provides the mean, standard deviation, median, trimmed mean, absolute deviation from the median, minimum and maximum.

library(psych)
describeBy(lapop18$ed, lapop18$fb_user)
## 
##  Descriptive statistics by group 
## group: 0
##    vars     n mean  sd median trimmed  mad min max range skew kurtosis   se
## X1    1 11540 8.06 4.3      8    7.99 4.45   0  18    18 0.13    -0.52 0.04
## ------------------------------------------------------------ 
## group: 1
##    vars     n  mean   sd median trimmed  mad min max range  skew kurtosis   se
## X1    1 14998 11.45 3.59     12   11.52 2.97   0  18    18 -0.24        0 0.03

This same information can be obtained using the tidyverse style of coding (with the operator pype %>%) and saved to a table. This table can save the data of the average age for WhatsApp users and non-users and also the standard deviation of each group. First, we define which dataframe to work with. Then, it is indicated that the missing values of the Whatsapp users variable are not used internally with filter(!is.na(wa_user)). Next, it is indicated that it will work in groups of the WhatsApp users variable with group_by(wa_user). Finally, it is indicated that in each group the mean and standard deviation will be calculated, with summarise.

library(dplyr)
whatxedad <- lapop18 %>%
  filter(!is.na(wa_user)) %>%
  group_by(wa_user) %>%
  summarise(promedio = mean(q2, na.rm=T), sd = sd(q2, na.rm=T))
whatxedad

Plotting a numeric variable by groups

The report does not show it, but graphs can be presented for each group to facilitate the comparison of a variable. To make these comparative graphs by group, we’re going to continue to use the tidyverse. As in the previous table, the dataframe is defined and it is indicated that the missing values of the “wa_user” variable are not taken into account. Then, it is instructed to make a plot, with ggplot having the variable “q2” on the X axis. This graph is defined to be a histogram with geom_histogram(). A novelty is that, with the facet_wrap(~wa_user) specification, it is possible to indicate that graphs are made for each group of that variable. Finally, the axes are labeled.

lapop18 %>%
  filter(!is.na(wa_user)) %>%
  ggplot(aes(x=q2))+
  geom_histogram()+
  facet_wrap(~wa_user)+
  xlab("Age")+
  ylab("Frecuency")

This graph, however, shows the values 0 and 1 of the variable “wa_user” in the header of both graphs. This is because this variable, when created, was defined by default as numeric. For the variable labels to appear, you have to transform “wa_user” into a factor and label it.

lapop18$wa_user = as.factor(lapop18$wa_user)
levels(lapop18$wa_user) <- c("No user", "User")

Other way to compare the age distribution by groups of WhatsApp users or non-users is by means of a boxplot. With the command boxplot you can make these plots. The command first asks for the variable on the Y axis, then the variable that defines the groups and the dataframe. You can label the X and Y axis with the names of the variables. Since the variable “wa_user” has been factored and labeled, the labels now appear.

boxplot(q2 ~ wa_user, data=lapop18, xlab ="Whatsapp user", ylab="Age")

Summary

In this section we have worked with numerical variables, such as age or years of study. Descriptive statistics have been calculated, such as the mean or standard deviation for the entire population or by groups. Finally, several ways of graphing these variables have been presented, using histograms or boxplots.

Calculations including survey weights

The above results do not include survey weights. To include weights in the calculations you can use the command weighted.mean, which is part of the library stats, which comes preloaded with R, so you don’t have to install it.

weighted.mean(lapop18$q2, lapop18$weight1500, na.rm=T)
## [1] 39.98095
weighted.mean(lapop18$ed, lapop18$weight1500, na.rm=T)
## [1] 9.931417
weighted.mean(lapop18$men, lapop18$weight1500, na.rm=T)*100
## [1] 49.74826
weighted.mean(lapop18$urban, lapop18$weight1500, na.rm=T)*100
## [1] 71.11895

Other way to calculate the mean including the expansion factor is by using the library survey and its native command svymean. For this you have to define the sample design with the command svydesign and save this design in an object, here called “design18”.

library(survey)
design18 = svydesign(ids = ~upm, strata = ~estratopri, weights = ~weight1500, nest=TRUE, data=lapop18)

To calculate the average, the command svymean is used and the na.rm=T specification is used because these variables have missing values.

svymean(~q2, design18, na.rm=T)
##      mean     SE
## q2 39.981 0.0535
svymean(~ed, design18, na.rm=T)
##      mean   SE
## ed 9.9314 0.04

For dummy variables, the procedure is the same, except that they are multiplied by 100 to present it in percentage format.

svymean(~men, design18, na.rm =T)*100
##       mean    SE
## men 49.748 8e-04
svymean(~urban, design18, na.rm=T)*100
##         mean     SE
## urban 71.119 0.0076

The package survey also has commands to replicate charts. For example, to calculate a simple histogram.

svyhist(~ed, design18, freq = T)

To calculate descriptive statistics by groups, you can use the command svyby, which allows you to define the numerical variable that you want to describe, the variable that defines the groups, and the weighted statistic that you want to calculate.

svyby(~ed, ~fb_user, design18, svymean, na.rm=T)

To reproduce a descriptive graph by groups, the command svyboxplot can be used to compare the distribution of the age variable between groups of a variable of type factor, such as WhatsApp users.

svyboxplot(~q2~factor(wa_user), design18, all.outliers = T)

LS0tCnRpdGxlOiAiRGVzY3JpcHRpdmUgc3RhdGlzdGljcyB1c2luZyB0aGUgQW1lcmljYXMgQmFyb21ldGVyICgzKSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29sbGFwc2VkOiBmYWxzZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgdG9jX2RlcHRoOiAxCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogZmxhdGx5CiAgICBkZl9wcmludDogcGFnZWQKICAgIHNlbGZfY29udGFpbmVkOiBubwogICAga2VlcF9tZDogeWVzCiAgICAjY29kZV9mb2xkaW5nOiBoaWRlCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiBzZW50ZW5jZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFKQpgYGAKCmBgYHtjc3MgY29sb3IsIGVjaG89RkFMU0V9Ci5jb2x1bW5zIHtkaXNwbGF5OiBmbGV4O30KaDEge2NvbG9yOiAjMzM2NkNDO30KYGBgCgojIEludHJvZHVjdGlvbgoKSW4gdGhpcyBzZWN0aW9uIHdlIHdpbGwgc2VlIGJhc2ljIGFzcGVjdHMgb2YgaG93IHRvIGRlc2NyaWJlIGEgbnVtZXJpY2FsIHZhcmlhYmxlLgpGb3IgdGhhdCwgd2Ugd2lsbCBjb250aW51ZSB0byB1c2UgdGhlIDIwMTgvMTkgcmVnaW9uYWwgcmVwb3J0ICJUaGUgUHVsc2Ugb2YgRGVtb2NyYWN5IiwgYXZhaWxhYmxlIFtoZXJlXShodHRwczovL3d3dy52YW5kZXJiaWx0LmVkdS9sYXBvcC9hYjIwMTgvMjAxOC0xOV9BbWVyaWNhc0Jhcm9tZXRlcl9SZWdpb25hbF9SZXBvcnRfMTAuMTMuMTkucGRmKSwgd2hlcmUgdGhlIG1haW4gZmluZGluZ3Mgb2YgdGhlIDIwMTgvMTkgcm91bmQgb2YgdGhlIEFtZXJpY2FzQmFyb21ldGVyIGFyZSBwcmVzZW50ZWQuCk9uZSBvZiB0aGUgc2VjdGlvbnMgb2YgdGhpcyBkb2N1bWVudCByZXBvcnRzIGluZm9ybWF0aW9uIG9uIHNvY2lhbCBuZXR3b3JrcyBhbmQgcG9saXRpY2FsIGF0dGl0dWRlcy4KVGhpcyBzZWN0aW9uIHByZXNlbnRzIGluZm9ybWF0aW9uIG9uIEludGVybmV0IHVzZSBhbmQgdGhlIHVzZSBvZiBzb2NpYWwgbmV0d29ya3MsIGluIGdlbmVyYWwsIGJ5IGNvdW50cnkgYW5kIGJ5IGNlcnRhaW4gc29jaW9kZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3MuCgojIEFib3V0IHRoZSBkYXRhc2V0CgpUaGUgZGF0YSB3ZSBhcmUgZ29pbmcgdG8gdXNlIHNob3VsZCBiZSBjaXRlZCBhcyBmb2xsb3dzOiBTb3VyY2U6IEFtZXJpY2FzQmFyb21ldGVyIGJ5IHRoZSBMYXRpbiBBbWVyaWNhbiBQdWJsaWMgT3BpbmlvbiBQcm9qZWN0IChMQVBPUCksIHd3d3cuTGFwb3BTdXJ2ZXlzLm9yZy4KVGhpcyBzZWN0aW9uIGxvYWRzIGEgdHJpbW1lZCBkYXRhYmFzZS4KVGhpcyBkYXRhYmFzZSBpcyBob3N0ZWQgaW4gdGhlICJtYXRlcmlhbHNfZWR1IiByZXBvc2l0b3J5IG9mIHRoZSBMQVBPUCBhY2NvdW50IG9uIEdpdEh1Yi4KSXQgaXMgcmVjb21tZW5kZWQgdG8gY2xlYW4gdGhlIEVudmlyb25tZW50IGJlZm9yZSBzdGFydGluZyB0aGlzIG1vZHVsZS4KClVzaW5nIHRoZSBsaWJyYXJ5IGByaW9gIGFuZCB0aGUgY29tbWFuZCBgaW1wb3J0YCwgeW91IGNhbiBpbXBvcnQgdGhpcyBkYXRhYmFzZSBmcm9tIHRoaXMgcmVwb3NpdG9yeS4KSW4gYWRkaXRpb24sIHRoZSBkYXRhIGZyb20gY291bnRyaWVzIHdpdGggY29kZXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDM1IGFyZSBzZWxlY3RlZCwgdGhhdCBpcywgdGhlIG9ic2VydmF0aW9ucyBvZiB0aGUgVW5pdGVkIFN0YXRlcyBhbmQgQ2FuYWRhIGFyZSBlbGltaW5hdGVkLgoKYGBge3IgYmFzZX0KbGlicmFyeShyaW8pCmxhcG9wMTggPSBpbXBvcnQoImh0dHBzOi8vcmF3LmdpdGh1Yi5jb20vbGFwb3AtY2VudHJhbC9tYXRlcmlhbHNfZWR1L21haW4vTEFQT1BfQUJfTWVyZ2VfMjAxOF92MS4wLnNhdiIpCmxhcG9wMTggPSBzdWJzZXQobGFwb3AxOCwgcGFpczw9MzUpCmBgYAoKIyBEZXNjcmliaW5nIGEgbnVtZXJpYyB2YXJpYWJsZQoKVGFibGUgMy4yIG9mIHRoZSByZXBvcnQgIlRoZSBQdWxzZSBvZiBEZW1vY3JhY3kiIHNob3dzIHRoZSBnZW5lcmFsIGF2ZXJhZ2VzIG9mIHRoZSB2YXJpYWJsZXMgYWdlICgicTIiIGluIHRoZSBkYXRhYmFzZSkgYW5kIHllYXJzIG9mIHN0dWR5ICgiZWQiIGluIHRoZSBkYXRhYmFzZSkgZm9yIHRoZSBnZW5lcmFsIHBvcHVsYXRpb24uCgohW10oVGFibGUlMjAzLjIuSlBHKXt3aWR0aD0iNjA0In0KClRoZSBjb21tYW5kIGBtZWFuYCBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBhbmQgYG5hLnJtPVRgIGlzIHVzZWQgYmVjYXVzZSB0aGVzZSB2YXJpYWJsZXMgaGF2ZSBtaXNzaW5nIHZhbHVlcy4KCmBgYHtyIG1lYW59Cm1lYW4obGFwb3AxOCRxMiwgbmEucm09VCkKbWVhbihsYXBvcDE4JGVkLCBuYS5ybT1UKQpgYGAKCkluIHRoZSBzZWN0aW9uIHdoZXJlIHdlIHdvcmtlZCB3aXRoIHF1YWxpdGF0aXZlIHZhcmlhYmxlcyAob3IgZmFjdG9yIHZhcmlhYmxlcywgaW4gdGhlIGxhbmd1YWdlIG9mIFIpLCB3ZSBzYXcgdGhhdCB0aGUgdmFyaWFibGVzICJtZW4iIGFuZCAidXJiYW4iIGNvdWxkIGJlIGRlc2NyaWJlZCBieSBkZWZpbmluZyB0aGVzZSB2YXJpYWJsZXMgYXMgYSBmYWN0b3IsIGxhYmVsaW5nIHRoZW0gYW5kIG1ha2luZyBhIGZyZXF1ZW5jeSB0YWJsZSBvZiB0aGVzZSB2YXJpYWJsZXMuCk90aGVyIHdheSB0byBmaW5kIHRoZSBwZXJjZW50YWdlIG9mIHBlb3BsZSB3aG8gYXJlIG1lbiBvciB3aG8gbGl2ZSBpbiB1cmJhbiBhcmVhcyBpcyB0byB3b3JrIHdpdGggdGhlc2UgdmFyaWFibGVzLCBidXQgbm90IGRlZmluZSB0aGVtIGFzIGEgZmFjdG9yLgpXaGVuIHZhcmlhYmxlcyBhcmUgY3JlYXRlZCwgdGhleSBhcmUgYm90aCBkZWZpbmVkIGJ5IGRlZmF1bHQgYXMgbnVtZXJpYy4KSW4gdGhpcyBjYXNlLCBpbiBhZGRpdGlvbiB0byBiZWluZyBudW1lcmljLCB0aGV5IGFyZSB2YXJpYWJsZXMgb2YgdHlwZSBkdW1teSwgdGhhdCBpcywgd2l0aCB2YWx1ZXMgMCBhbmQgMS4KSW4gdGhlIGNhc2Ugb2YgdGhlICJtZW4iIHZhcmlhYmxlLCB3ZSBkZWZpbmVkIDAgPSBXb21hbiBhbmQgMSA9IE1hbjsgYW5kIGluIHRoZSBjYXNlIG9mIHRoZSAidXJiYW4iIHZhcmlhYmxlIHdlIGRlZmluZSAwID0gUnVyYWwgYW5kIDEgPSBVcmJhbi4KSXQgaXMgYSBnb29kIHByYWN0aWNlIHRvIG5hbWUgdGhlIGR1bW15IHZhcmlhYmxlIHdpdGggYSBuYW1lIHRoYXQgcmVmZXJzIHRvIGNhdGVnb3J5IDEuCldpdGggZHVtbXkgdmFyaWFibGVzLCB3aGVuIHRoZSBhdmVyYWdlIGlzIGNhbGN1bGF0ZWQsIHRoZSByZXN1bHQgaXMgdGhlIHNhbWUgYXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY2F0ZWdvcnkgMS4KU28sIGlmIGBtZWFuKGxhcG9wJG1hbiwgbmEucm09VClgIGlzIGNhbGN1bGF0ZWQsIHRoaXMgb3BlcmF0aW9uIGdpdmVzIHVzIHRoZSBwZXJjZW50YWdlIG9mIGNhdGVnb3J5IDEsIHRoYXQgaXMsIG1lbi4KSXQgaXMgbXVsdGlwbGllZCBieSAxMDAgdG8gcHV0IGl0IGluIGEgZm9ybWF0IGZyb20gMCB0byAxMDAuCgpgYGB7ciBkdW1teSBhdmVyYWdlfQpsYXBvcDE4JG1lbiA9IDItbGFwb3AxOCRxMQpsYXBvcDE4JHVyYmFuID0gMi1sYXBvcDE4JHVyCm1lYW4obGFwb3AxOCRtZW4sIG5hLnJtPVQpKjEwMAptZWFuKGxhcG9wMTgkdXJiYW4sIG5hLnJtPVQpKjEwMApgYGAKClRoZXNlIHJlc3VsdHMgYXJlIHRoZSBzYW1lIGFzIHRob3NlIHByZXNlbnRlZCBpbiB0aGUgZmlyc3QgY29sdW1uIG9mIHJlc3VsdHMgZm9yIHRoZSBnZW5lcmFsIHBvcHVsYXRpb24sIGV4Y2VwdCBmb3IgdGhlIHdlYWx0aCB2YXJpYWJsZSAoInF1aW50YWxsIikgd2hpY2ggaXMgbm90IGF2YWlsYWJsZSBpbiB0aGlzIHRyaW1tZWQgdmVyc2lvbiBvZiB0aGUgZGF0YXNldC4KCiMgUGxvdHRpbmcgYSBudW1lcmljIHZhcmlhYmxlCgpBZnRlciBkZXNjcmliaW5nIGEgbnVtZXJpYyB2YXJpYWJsZSwgeW91IGNhbiBhbHNvIGluY2x1ZGUgc29tZSBiYXNpYyBwbG90cywgZm9yIGV4YW1wbGUsIHVzaW5nIHRoZSBjb21tYW5kIGBoaXN0YCB5b3UgY2FuIHByb2R1Y2UgdGhlIGhpc3RvZ3JhbSBvZiB0aGUgdmFyaWFibGUgInllYXJzIG9mIGVkdWNhdGlvbiIgKGVkKS4KCmBgYHtyIHNpbXBsZSBoaXN0b2dyYW19Cmhpc3QobGFwb3AxOCRlZCkKYGBgCgpUaGlzIHNhbWUgcGxvdCBjYW4gYmUgcmVwcm9kdWNlZCB1c2luZyB0aGUgY29tbWFuZCBgZ2dwbG90YC4KV2l0aCB0aGlzIGNvbW1hbmQgd2UgaGF2ZSBtb3JlIGZsZXhpYmlsaXR5IHdpdGggdGhlIGdyYXBoaWMgb3B0aW9ucy4KRmlyc3QsIHdlIGRlZmluZSB0aGUgZGF0YWZyYW1lIHRvIGJlIHVzZWQgImxhcG9wMTgiIGFuZCB0aGUgdmFyaWFibGUgImVkIiBvbiB0aGUgWCBheGlzLgpUaGVuIHdlIGRlZmluZSB0aGUgc3BlY2lmaWNhdGlvbiBgZ2VvbV9oaXN0b2dyYW0oKWAgdG8gcHJvZHVjZSBhIGhpc3RvZ3JhbS4KV2UgZGVmaW5lIHRoZSB3aWR0aCBvZiB0aGUgaGlzdG9ncmFtIGJhciB3aXRoIGBiYW53aWR0aD0xYC4KRmluYWxseSwgdGhpcyBjb2RlIGFsbG93cyB5b3UgdG8gbGFiZWwgdGhlIFggYW5kIFkgYXhpcyBhbmQgaW5jbHVkZSBhIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZSwgd2l0aCBgdGhlbWVfYncoKWAuCgpgYGB7ciBnZ2hpc3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGxhcG9wMTgsIGFlcyh4PWVkKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSsKICB4bGFiKCJZZWFycyBvZiBlZHVjYXRpb24iKSsKICB5bGFiKCJGcmVjdWVuY3kiKSsKICB0aGVtZV9idygpCmBgYAoKIyBBdmVyYWdlIGJ5IGdyb3VwcwoKVGFibGUgMy4yIG9mIHRoZSByZXBvcnQgc2hvd3MgdGhlIG1lYW4gb2YgdGhlc2UgbnVtZXJpY2FsIHZhcmlhYmxlcyBieSBncm91cHMgb2YgdmFyaWFibGVzIHJlbGF0ZWQgdG8gc29jaWFsIG5ldHdvcmtzLgpGb3IgZXhhbXBsZSwgaXQgc2hvd3MgdGhlIGF2ZXJhZ2Ugb2YgeWVhcnMgb2Ygc3R1ZHkgZm9yIEZhY2Vib29rIHVzZXJzIGFuZCBmb3Igbm9uLUZhY2Vib29rIHVzZXJzLgpJZiB3ZSB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBvZiB5ZWFycyBvZiBzdHVkeSBmb3IgRmFjZWJvb2sgdXNlcnMsIHdlIGZpcnN0IGNhbGN1bGF0ZSB0aGlzIHZhcmlhYmxlLCBpbiB0aGUgc2FtZSB3YXkgYXMgaW4gcHJldmlvdXMgc2VjdGlvbnMsIHdpdGggdGhlIGNvbW1hbmQgYGlmZWxzZWAuCgpgYGB7ciB1c2Vyc30KbGFwb3AxOCRmYl91c2VyIDwtIGlmZWxzZShsYXBvcDE4JHNtZWRpYTE9PTEgJiBsYXBvcDE4JHNtZWRpYTI8PTQsIDEsIDApCmxhcG9wMTgkdHdfdXNlciA8LSBpZmVsc2UobGFwb3AxOCRzbWVkaWE0PT0xICYgbGFwb3AxOCRzbWVkaWE1PD00LCAxLCAwKQpsYXBvcDE4JHdhX3VzZXIgPC0gaWZlbHNlKGxhcG9wMTgkc21lZGlhNz09MSAmIGxhcG9wMTgkc21lZGlhODw9NCwgMSwgMCkKYGBgCgpDYWxjdWxhdGluZyB0aGUgYXZlcmFnZSB5ZWFycyBmb3IgRmFjZWJvb2sgdXNlcnMgYW5kIG5vbi11c2VycyBjYW4gYmUgZG9uZSBpbiBtYW55IHdheXMuCkEgZmlyc3Qgb3B0aW9uIGlzIGJ5IHVzaW5nIHRoZSBzcXVhcmUgYnJhY2tldHMgYFsuLi5dYC4KSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSB5ZWFycyBvZiBzdHVkeSBieSB1c2VyIGdyb3VwcyBgW2xhcG9wMTgkZmJfdXNlcj09MV1gIGFuZCBub24tRmFjZWJvb2sgdXNlcnMgYFtsYXBvcDE4JGZiX3VzZXI9PTBdYC4KCmBgYHtyIEZiIHN0dWR5IHllYXJzfQptZWFuKGxhcG9wMTgkZWRbbGFwb3AxOCRmYl91c2VyPT0wXSwgbmEucm09VCkKbWVhbihsYXBvcDE4JGVkW2xhcG9wMTgkZmJfdXNlcj09MV0sIG5hLnJtPVQpCmBgYAoKIyBEZXNjcmliaW5nIG9mIGEgbnVtZXJpY2FsIHZhcmlhYmxlIGJ5IGdyb3VwcwoKT3RoZXIgd2F5IHRvIGRlc2NyaWJlIGEgbnVtZXJpYyB2YXJpYWJsZSBpcyBieSB1c2luZyB0aGUgY29tbWFuZCBgc3VtbWFyeWAuClRoaXMgY29tbWFuZCByZXBvcnRzIHRoZSBtb3N0IHVzZWQgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBmb3IgYSBudW1lcmljYWwgdmFyaWFibGU6IG1pbmltdW0sIG1heGltdW0sIHF1YXJ0aWxlcywgbWVhbiBhbmQgbWVkaWFuLgpBbGwgdGhlc2Ugc3RhdGlzdGljcyBhbGxvdyBhIGJldHRlciBjb21wYXJpc29uIGJldHdlZW4gYm90aCBncm91cHMsIHVzZXJzIGFuZCBub24tdXNlcnMgb2YgRmFjZWJvb2suCldpdGhpbiB0aGlzIGNvbW1hbmQgeW91IGNhbiBpbmNsdWRlIHRoZSBgZGlnaXRzPTNgIHNwZWNpZmljYXRpb24gdG8gcm91bmQgdGhlIHJlc3VsdHMsIHdoaWNoIGF2b2lkcyBoYXZpbmcgdG8gdXNlIGByb3VuZGAsIGZvciBleGFtcGxlLgoKYGBge3J9CnN1bW1hcnkobGFwb3AxOCRlZFtsYXBvcDE4JGZiX3VzZXI9PTBdLCBuYS5ybT1ULCBkaWdpdHM9MykKc3VtbWFyeShsYXBvcDE4JGVkW2xhcG9wMTgkZmJfdXNlcj09MV0sIG5hLnJtPVQsIGRpZ2l0cz0zKQpgYGAKCkhvd2V2ZXIsIHRoZSBjb21tYW5kIGBzdW1tYXJ5YCBkb2VzIG5vdCByZXR1cm4gYW4gaW1wb3J0YW50IHN0YXRpc3RpYyBzdWNoIGFzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24sIGEgbWVhc3VyZSBvZiBkaXNwZXJzaW9uIG9yIGhldGVyb2dlbmVpdHkuCkluIG9yZGVyIHRvIGhhdmUgdGhlIGFib3ZlIHN0YXRpc3RpY3MgYW5kIGluY2x1ZGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiwgYW1vbmcgb3RoZXIgYWRkaXRpb25hbCBtZWFzdXJlbWVudHMsIHlvdSBjYW4gdXNlIHRoZSBjb21tYW5kIGBkZXNjcmliZUJ5YCwgd2hpY2ggaXMgcGFydCBvZiB0aGUgbGlicmFyeSBgcHN5Y2hgLgpUaGlzIGNvbW1hbmQgYXNrcyBmb3IgdGhlIHZhcmlhYmxlIHRvIGRlc2NyaWJlICgiZWQiKSBhbmQgdGhlIHZhcmlhYmxlIHRoYXQgZm9ybXMgdGhlIGdyb3VwcyAoImZiX3VzZXIiKSBhbmQgcHJvdmlkZXMgdGhlIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgbWVkaWFuLCB0cmltbWVkIG1lYW4sIGFic29sdXRlIGRldmlhdGlvbiBmcm9tIHRoZSBtZWRpYW4sIG1pbmltdW0gYW5kIG1heGltdW0uCgpgYGB7ciBhdmVyYWdlIHllYXJzIG9mIHN0dWR5IGJ5IEZCLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHBzeWNoKQpkZXNjcmliZUJ5KGxhcG9wMTgkZWQsIGxhcG9wMTgkZmJfdXNlcikKYGBgCgpUaGlzIHNhbWUgaW5mb3JtYXRpb24gY2FuIGJlIG9idGFpbmVkIHVzaW5nIHRoZSB0aWR5dmVyc2Ugc3R5bGUgb2YgY29kaW5nICh3aXRoIHRoZSBvcGVyYXRvciBweXBlIGAlPiVgKSBhbmQgc2F2ZWQgdG8gYSB0YWJsZS4KVGhpcyB0YWJsZSBjYW4gc2F2ZSB0aGUgZGF0YSBvZiB0aGUgYXZlcmFnZSBhZ2UgZm9yIFdoYXRzQXBwIHVzZXJzIGFuZCBub24tdXNlcnMgYW5kIGFsc28gdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBlYWNoIGdyb3VwLgpGaXJzdCwgd2UgZGVmaW5lIHdoaWNoIGRhdGFmcmFtZSB0byB3b3JrIHdpdGguClRoZW4sIGl0IGlzIGluZGljYXRlZCB0aGF0IHRoZSBtaXNzaW5nIHZhbHVlcyBvZiB0aGUgV2hhdHNhcHAgdXNlcnMgdmFyaWFibGUgYXJlIG5vdCB1c2VkIGludGVybmFsbHkgd2l0aCBgZmlsdGVyKCFpcy5uYSh3YV91c2VyKSlgLgpOZXh0LCBpdCBpcyBpbmRpY2F0ZWQgdGhhdCBpdCB3aWxsIHdvcmsgaW4gZ3JvdXBzIG9mIHRoZSBXaGF0c0FwcCB1c2VycyB2YXJpYWJsZSB3aXRoIGBncm91cF9ieSh3YV91c2VyKWAuCkZpbmFsbHksIGl0IGlzIGluZGljYXRlZCB0aGF0IGluIGVhY2ggZ3JvdXAgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiB3aWxsIGJlIGNhbGN1bGF0ZWQsIHdpdGggYHN1bW1hcmlzZWAuCgpgYGB7ciBhdmVyYWdlIHllYXJzIHggV2hhdHNhcHAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZHBseXIpCndoYXR4ZWRhZCA8LSBsYXBvcDE4ICU+JQogIGZpbHRlcighaXMubmEod2FfdXNlcikpICU+JQogIGdyb3VwX2J5KHdhX3VzZXIpICU+JQogIHN1bW1hcmlzZShwcm9tZWRpbyA9IG1lYW4ocTIsIG5hLnJtPVQpLCBzZCA9IHNkKHEyLCBuYS5ybT1UKSkKd2hhdHhlZGFkCmBgYAoKIyBQbG90dGluZyBhIG51bWVyaWMgdmFyaWFibGUgYnkgZ3JvdXBzCgpUaGUgcmVwb3J0IGRvZXMgbm90IHNob3cgaXQsIGJ1dCBncmFwaHMgY2FuIGJlIHByZXNlbnRlZCBmb3IgZWFjaCBncm91cCB0byBmYWNpbGl0YXRlIHRoZSBjb21wYXJpc29uIG9mIGEgdmFyaWFibGUuClRvIG1ha2UgdGhlc2UgY29tcGFyYXRpdmUgZ3JhcGhzIGJ5IGdyb3VwLCB3ZSdyZSBnb2luZyB0byBjb250aW51ZSB0byB1c2UgdGhlIHRpZHl2ZXJzZS4KQXMgaW4gdGhlIHByZXZpb3VzIHRhYmxlLCB0aGUgZGF0YWZyYW1lIGlzIGRlZmluZWQgYW5kIGl0IGlzIGluZGljYXRlZCB0aGF0IHRoZSBtaXNzaW5nIHZhbHVlcyBvZiB0aGUgIndhX3VzZXIiIHZhcmlhYmxlIGFyZSBub3QgdGFrZW4gaW50byBhY2NvdW50LgpUaGVuLCBpdCBpcyBpbnN0cnVjdGVkIHRvIG1ha2UgYSBwbG90LCB3aXRoIGBnZ3Bsb3RgIGhhdmluZyB0aGUgdmFyaWFibGUgInEyIiBvbiB0aGUgWCBheGlzLgpUaGlzIGdyYXBoIGlzIGRlZmluZWQgdG8gYmUgYSBoaXN0b2dyYW0gd2l0aCBgZ2VvbV9oaXN0b2dyYW0oKWAuCkEgbm92ZWx0eSBpcyB0aGF0LCB3aXRoIHRoZSBgZmFjZXRfd3JhcCh+d2FfdXNlcilgIHNwZWNpZmljYXRpb24sIGl0IGlzIHBvc3NpYmxlIHRvIGluZGljYXRlIHRoYXQgZ3JhcGhzIGFyZSBtYWRlIGZvciBlYWNoIGdyb3VwIG9mIHRoYXQgdmFyaWFibGUuCkZpbmFsbHksIHRoZSBheGVzIGFyZSBsYWJlbGVkLgoKYGBge3IgaGlzdCBlZGFkeHdoYXQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxhcG9wMTggJT4lCiAgZmlsdGVyKCFpcy5uYSh3YV91c2VyKSkgJT4lCiAgZ2dwbG90KGFlcyh4PXEyKSkrCiAgZ2VvbV9oaXN0b2dyYW0oKSsKICBmYWNldF93cmFwKH53YV91c2VyKSsKICB4bGFiKCJBZ2UiKSsKICB5bGFiKCJGcmVjdWVuY3kiKQpgYGAKClRoaXMgZ3JhcGgsIGhvd2V2ZXIsIHNob3dzIHRoZSB2YWx1ZXMgMCBhbmQgMSBvZiB0aGUgdmFyaWFibGUgIndhX3VzZXIiIGluIHRoZSBoZWFkZXIgb2YgYm90aCBncmFwaHMuClRoaXMgaXMgYmVjYXVzZSB0aGlzIHZhcmlhYmxlLCB3aGVuIGNyZWF0ZWQsIHdhcyBkZWZpbmVkIGJ5IGRlZmF1bHQgYXMgbnVtZXJpYy4KRm9yIHRoZSB2YXJpYWJsZSBsYWJlbHMgdG8gYXBwZWFyLCB5b3UgaGF2ZSB0byB0cmFuc2Zvcm0gIndhX3VzZXIiIGludG8gYSBmYWN0b3IgYW5kIGxhYmVsIGl0LgoKYGBge3Igd2EgZmFjdG9yfQpsYXBvcDE4JHdhX3VzZXIgPSBhcy5mYWN0b3IobGFwb3AxOCR3YV91c2VyKQpsZXZlbHMobGFwb3AxOCR3YV91c2VyKSA8LSBjKCJObyB1c2VyIiwgIlVzZXIiKQpgYGAKCk90aGVyIHdheSB0byBjb21wYXJlIHRoZSBhZ2UgZGlzdHJpYnV0aW9uIGJ5IGdyb3VwcyBvZiBXaGF0c0FwcCB1c2VycyBvciBub24tdXNlcnMgaXMgYnkgbWVhbnMgb2YgYSBib3hwbG90LgpXaXRoIHRoZSBjb21tYW5kIGBib3hwbG90YCB5b3UgY2FuIG1ha2UgdGhlc2UgcGxvdHMuClRoZSBjb21tYW5kIGZpcnN0IGFza3MgZm9yIHRoZSB2YXJpYWJsZSBvbiB0aGUgWSBheGlzLCB0aGVuIHRoZSB2YXJpYWJsZSB0aGF0IGRlZmluZXMgdGhlIGdyb3VwcyBhbmQgdGhlIGRhdGFmcmFtZS4KWW91IGNhbiBsYWJlbCB0aGUgWCBhbmQgWSBheGlzIHdpdGggdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMuClNpbmNlIHRoZSB2YXJpYWJsZSAid2FfdXNlciIgaGFzIGJlZW4gZmFjdG9yZWQgYW5kIGxhYmVsZWQsIHRoZSBsYWJlbHMgbm93IGFwcGVhci4KCmBgYHtyIGJveHBsb3QgZWRhZHhXaGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmJveHBsb3QocTIgfiB3YV91c2VyLCBkYXRhPWxhcG9wMTgsIHhsYWIgPSJXaGF0c2FwcCB1c2VyIiwgeWxhYj0iQWdlIikKYGBgCgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiB3ZSBoYXZlIHdvcmtlZCB3aXRoIG51bWVyaWNhbCB2YXJpYWJsZXMsIHN1Y2ggYXMgYWdlIG9yIHllYXJzIG9mIHN0dWR5LgpEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGhhdmUgYmVlbiBjYWxjdWxhdGVkLCBzdWNoIGFzIHRoZSBtZWFuIG9yIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgdGhlIGVudGlyZSBwb3B1bGF0aW9uIG9yIGJ5IGdyb3Vwcy4KRmluYWxseSwgc2V2ZXJhbCB3YXlzIG9mIGdyYXBoaW5nIHRoZXNlIHZhcmlhYmxlcyBoYXZlIGJlZW4gcHJlc2VudGVkLCB1c2luZyBoaXN0b2dyYW1zIG9yIGJveHBsb3RzLgoKIyBDYWxjdWxhdGlvbnMgaW5jbHVkaW5nIHN1cnZleSB3ZWlnaHRzCgpUaGUgYWJvdmUgcmVzdWx0cyBkbyBub3QgaW5jbHVkZSBzdXJ2ZXkgd2VpZ2h0cy4KVG8gaW5jbHVkZSB3ZWlnaHRzIGluIHRoZSBjYWxjdWxhdGlvbnMgeW91IGNhbiB1c2UgdGhlIGNvbW1hbmQgYHdlaWdodGVkLm1lYW5gLCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBsaWJyYXJ5IGBzdGF0c2AsIHdoaWNoIGNvbWVzIHByZWxvYWRlZCB3aXRoIFIsIHNvIHlvdSBkb24ndCBoYXZlIHRvIGluc3RhbGwgaXQuCgpgYGB7ciBjb21hbmQgd2VpZ2h0ZWR9CndlaWdodGVkLm1lYW4obGFwb3AxOCRxMiwgbGFwb3AxOCR3ZWlnaHQxNTAwLCBuYS5ybT1UKQp3ZWlnaHRlZC5tZWFuKGxhcG9wMTgkZWQsIGxhcG9wMTgkd2VpZ2h0MTUwMCwgbmEucm09VCkKd2VpZ2h0ZWQubWVhbihsYXBvcDE4JG1lbiwgbGFwb3AxOCR3ZWlnaHQxNTAwLCBuYS5ybT1UKSoxMDAKd2VpZ2h0ZWQubWVhbihsYXBvcDE4JHVyYmFuLCBsYXBvcDE4JHdlaWdodDE1MDAsIG5hLnJtPVQpKjEwMApgYGAKCk90aGVyIHdheSB0byBjYWxjdWxhdGUgdGhlIG1lYW4gaW5jbHVkaW5nIHRoZSBleHBhbnNpb24gZmFjdG9yIGlzIGJ5IHVzaW5nIHRoZSBsaWJyYXJ5IGBzdXJ2ZXlgIGFuZCBpdHMgbmF0aXZlIGNvbW1hbmQgYHN2eW1lYW5gLgpGb3IgdGhpcyB5b3UgaGF2ZSB0byBkZWZpbmUgdGhlIHNhbXBsZSBkZXNpZ24gd2l0aCB0aGUgY29tbWFuZCBgc3Z5ZGVzaWduYCBhbmQgc2F2ZSB0aGlzIGRlc2lnbiBpbiBhbiBvYmplY3QsIGhlcmUgY2FsbGVkICJkZXNpZ24xOCIuCgpgYGB7ciBzdXJ2ZXksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc3VydmV5KQpkZXNpZ24xOCA9IHN2eWRlc2lnbihpZHMgPSB+dXBtLCBzdHJhdGEgPSB+ZXN0cmF0b3ByaSwgd2VpZ2h0cyA9IH53ZWlnaHQxNTAwLCBuZXN0PVRSVUUsIGRhdGE9bGFwb3AxOCkKYGBgCgpUbyBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UsIHRoZSBjb21tYW5kIGBzdnltZWFuYCBpcyB1c2VkIGFuZCB0aGUgYG5hLnJtPVRgIHNwZWNpZmljYXRpb24gaXMgdXNlZCBiZWNhdXNlIHRoZXNlIHZhcmlhYmxlcyBoYXZlIG1pc3NpbmcgdmFsdWVzLgoKYGBge3Igd2VpZ2h0ZWQgbWVhbiBhZ2UgYW5kIHN0dWRpZXN9CnN2eW1lYW4ofnEyLCBkZXNpZ24xOCwgbmEucm09VCkKc3Z5bWVhbih+ZWQsIGRlc2lnbjE4LCBuYS5ybT1UKQpgYGAKCkZvciBkdW1teSB2YXJpYWJsZXMsIHRoZSBwcm9jZWR1cmUgaXMgdGhlIHNhbWUsIGV4Y2VwdCB0aGF0IHRoZXkgYXJlIG11bHRpcGxpZWQgYnkgMTAwIHRvIHByZXNlbnQgaXQgaW4gcGVyY2VudGFnZSBmb3JtYXQuCgpgYGB7ciB3ZWlnaHRlZCBtZWFuIG1hbiBhbmQgdXJiYW59CnN2eW1lYW4ofm1lbiwgZGVzaWduMTgsIG5hLnJtID1UKSoxMDAKc3Z5bWVhbih+dXJiYW4sIGRlc2lnbjE4LCBuYS5ybT1UKSoxMDAKYGBgCgpUaGUgcGFja2FnZSBgc3VydmV5YCBhbHNvIGhhcyBjb21tYW5kcyB0byByZXBsaWNhdGUgY2hhcnRzLgpGb3IgZXhhbXBsZSwgdG8gY2FsY3VsYXRlIGEgc2ltcGxlIGhpc3RvZ3JhbS4KCmBgYHtyIHdlaWdodGVkIGhpc3R9CnN2eWhpc3QofmVkLCBkZXNpZ24xOCwgZnJlcSA9IFQpCmBgYAoKVG8gY2FsY3VsYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgYnkgZ3JvdXBzLCB5b3UgY2FuIHVzZSB0aGUgY29tbWFuZCBgc3Z5YnlgLCB3aGljaCBhbGxvd3MgeW91IHRvIGRlZmluZSB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlIHRoYXQgeW91IHdhbnQgdG8gZGVzY3JpYmUsIHRoZSB2YXJpYWJsZSB0aGF0IGRlZmluZXMgdGhlIGdyb3VwcywgYW5kIHRoZSB3ZWlnaHRlZCBzdGF0aXN0aWMgdGhhdCB5b3Ugd2FudCB0byBjYWxjdWxhdGUuCgpgYGB7ciB3ZWlnaHRlZCBlZCBieSBncm91cHN9CnN2eWJ5KH5lZCwgfmZiX3VzZXIsIGRlc2lnbjE4LCBzdnltZWFuLCBuYS5ybT1UKQpgYGAKClRvIHJlcHJvZHVjZSBhIGRlc2NyaXB0aXZlIGdyYXBoIGJ5IGdyb3VwcywgdGhlIGNvbW1hbmQgYHN2eWJveHBsb3RgIGNhbiBiZSB1c2VkIHRvIGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgYWdlIHZhcmlhYmxlIGJldHdlZW4gZ3JvdXBzIG9mIGEgdmFyaWFibGUgb2YgdHlwZSBmYWN0b3IsIHN1Y2ggYXMgV2hhdHNBcHAgdXNlcnMuCgpgYGB7ciB3ZWlnaHRlZCBib3hwbG90IGJ5IGdyb3Vwc30Kc3Z5Ym94cGxvdCh+cTJ+ZmFjdG9yKHdhX3VzZXIpLCBkZXNpZ24xOCwgYWxsLm91dGxpZXJzID0gVCkKYGBgCg==