1 Motivation

Creative Commons License

  • We live in a big data era
  • Data on location, clicks, e-commerce, social media …
  • Life Sciences: measure expression of thousands of genes, proteins, … for each subject or even individual cells
  • Data driven journalism

Statistics is the science to learn from empirical data.

Statistical literacy is key to interpret results from scientific publications.

There are three types of lies: lies, damn lies and statistics


There are three types of lies: lies, damn lies and bad statistics


https://www.medrxiv.org/content/10.1101/2020.07.17.20155846v1



“The authors state: The negative ecological association between COVID-19 mortality and the consumption of cabbage and cucumber supports the a priori hypothesis previously reported. In this hypothesis, we proposed that vegetables such as Brassica - with an antioxidant activity reducing insulin resistance - may also be associated with low COVID-19 mortality in countries.”

" Though our results do not allow to infer causality, they do reinforce our a priory hypothesis that the ingestion of anti-oxidant foods acting on insulin intolerance may have reduced the severity of COVID-19. "



  • Many hypotheses are assessed ?!
  • Causality ?!
  • Experimental design: Observational study
  • Based on the data we cannot provide recommendations at the subject level

  • Importance of Data Exploration!
  • Data does not exhibit the trend
  • Data shows evidence for two clusters: above and below 200 deaths/million
  • Model for cucumber does not model the data correctly: Overestimation of death rate for many countries e.g. Portugal, Hungary, …

Papers are merged, published, and 39 times cited

Bousquet et al. (2021). Cabbage and fermented vegetables: From death rate heterogeneity in countries to candidates for mitigation strategies of severe COVID-19. Allergy 76:735–750.


In the example we covered issues with three important branches in statistics

  1. Statistical inference
  • Is there an association between COVID-19 mortality and food consumption?
  • Issue in study:
    • data dredging, p-hacking, …: when you assess many hypotheses you will allways find strong patterns by random change \(\rightarrow\) correct for multiple testing!
    • Assumptions of the models do not hold
    • Confounding
  1. Experimental design

    • Confounding: countries do not only differ in consumption of a vegetable but also in may other variables (demographical, COVID measures, healty care, …) that are associated with COVID mortality.

    • Difficult to draw causal conclusions from observational studies.

    • In experimental studies: randomisation! \(\rightarrow\) so that the groups only differ in the treatment.

    • Experimental studies are therefore the golden standard

  2. Data exploration and visualisation

    • Crucial to get insight in the data!
    • Assess model assumptions

2 Smelly armpit example

  • Smelly armpits are not caused by sweat itself. The smell is caused by specific micro-organisms belonging to the group of Corynebacterium spp. that metabolise sweat. Another group of abundant bacteria are the Staphylococcus spp., these bacteria do not metabolise sweat in smelly compounds.

  • The CMET-groep at Ghent University does research on transplanting the armpit microbiome to save people with smelly armpits.

  • Proposed Therapy:

    1. Remove armpit-microbiome with antibiotics
    2. Influence armpit microbiome with microbial transplant (https://youtu.be/9RIFyqLXdVw)

  • Experiment:

    • 20 subjects with smelly armpits are attributed to one of two treatment groups
    • placebo (only antibiotics)
    • transplant (antibiotics followed by microbial transplant).
    • The microbiome is sampled 6 weeks upon the treatment.
    • The relative abundance of Staphylococcus spp. on Corynebacterium spp. + Staphylococcus spp. in the microbiome is measured via DGGE (Denaturing Gradient Gel Electrophoresis).

2.1 Import the data

read_lines("https://raw.githubusercontent.com/GTPB/PSLS20/master/data/armpit.csv")
 [1] "trt,rel"                       "placebo,54.99207606973059"    
 [3] "placebo,31.84466019417476"     "placebo,41.09947643979057"    
 [5] "placebo,59.52063914780293"     "placebo,63.573407202216075"   
 [7] "placebo,41.48648648648649"     "placebo,30.44041450777202"    
 [9] "placebo,42.95676429567643"     "placebo,41.7391304347826"     
[11] "placebo,33.896515311510036"    "transplant,57.218124341412015"
[13] "transplant,72.50900360144058"  "transplant,61.89258312020461" 
[15] "transplant,56.690140845070424" "transplant,76"                
[17] "transplant,71.7357910906298"   "transplant,57.757296466973884"
[19] "transplant,65.1219512195122"   "transplant,67.53424657534246" 
[21] "transplant,77.55359394703657" 

The file is comma separated and in tidy format

ap<-read_csv("https://raw.githubusercontent.com/GTPB/PSLS20/master/data/armpit.csv")
ap

2.2 Data Exploration and Descriptive Statistics

  • Data exploration is extremely important to get insight in the data.
  • It is often underrated and overlooked.

2.2.1 Descriptive statistics

We first summarize the data and calculate the mean, standard deviation, number of observations and standard error and store the result in an object apRelSum via ’apRelSum<-`

  1. We pipe the ap dataframe to the group_by function to group the data by treatment trt group_by(trt)
  2. We pipe the result to the summarize_at function to summarize the “rel” variable and calculate the mean, standard deviation and the number of observations
  3. We pipe the result to the mutate function to make a new variable in the data frame se for which we calculate the standard error
apRelSum<-ap%>%
  group_by(trt)%>%
  summarize_at("rel",
               list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))

apRelSum

2.2.2 Plots

We will use ggplot2 to make our plots. With the ggplot2 library we can easily build plots by adding layers.

2.2.2.1 barplot

  1. We pipe our summarized data to the ggplot function and we select the treatment variable trt and the variable mean for plotting aes(x=trt,y=mean)

  2. We make a barplot based on this data using the geom_bar function. The statistic is stat="identity" because the bar height should be equal the value for the mean of the relative abundance.

apRelSum%>% 
  ggplot(aes(x=trt,y=mean)) +
  geom_bar(stat="identity") 

  • Is this plot informative??

We will now add standard errors to the plot using geom_errorbar function and specify the minimum and maximum value for of the error bar, the width command is used to set the width of the error bar smaller than the width of the bar.

apRelSum%>% 
  ggplot(aes(x=trt,y=mean)) +
  geom_bar(stat="identity") + 
  geom_errorbar(aes(ymin=mean-se,ymax=mean+se),width=.2)

  • Is this plot informative??

2.2.2.2 boxplots

I consider barplots to be bad plots

  • They are not informative
  • They just visualize a two point summary of the data. It is better to do this in a table
  • They use a lot of space (e.g. from zero up to the minimum relative abundance) where no data are present.

It is better to get a view on the distribution of the data. We can use a boxplot for this purpose. We first explain what a boxplot.



We will now make a boxplot for the ap data

  1. We pipe the ap dataframe to the ggplot command
  2. We select the data with the command ggplot(aes(x=trt,y=rel))
  3. We add a boxplot with the command geom_boxplot()
ap %>%  
  ggplot(aes(x=trt,y=rel)) + 
  geom_boxplot()


  • Note, that we do not have so many observations.

  • It is always better to show the data as raw as possible!

We will now add the raw data to the plot.

  • Note that we set the outlier.shape=NA in the geom_boxplot function because because we will add all raw data anyway.
  • We add the raw data using geom_point(position="jitter"), with the argument position=‘jitter’ we will add some random noise to the x coordinate so that we can see all data.
ap %>%  
  ggplot(aes(x=trt,y=rel)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(position="jitter")

This is an informative plot!


  • We observed an effect of the transplantation on the relatieve abundantie of Staphylococcus.

  • Is that effect large enough to conclude that the treatment works?


2.3 Estimation and statistical inference

  • Induction: With statistical inference we can generalize what we observe in the sample towards the population.

  • The price that we have to pay: uncertainty on our conclusions!


  • With data we cannot prove that the treatment works

  • Falsification principle of Popper: With data we can only reject a hypothesis or theory.

  • With stats we can thus not prove that the treatment works.

  • But stats will allow us to falcify the opposite hypothesis: how much evidence is there in the data against the assumption that there is no effect of the treatment?

  • With stats we can calculate how likely it is to draw a random sample (when you would repeat the experiment) with a mean difference in relative abundance between transplant and placebo group that is at least as large as what we observed in our sample when there would be no effect of the treatment.

  • This probability is called a p-value.

  • If p is very small, it is very unlikely to observe a sample like ours by random change when there would be no effect of the treatment.

  • We typically compare p with 5%. If there is no effect of the treatment we will thus tolerate a probability of 5% on a false positive conclusion.

  • To calculate p we will have to model the data using statistical models.


In chapter 5 we will learn that we can use a two-sample t-test to generalise what we observe in the microbiome dataset towards the population.

t.test(rel~trt,data=ap)

    Welch Two Sample t-test

data:  rel by trt
t = -5.0334, df = 15.892, p-value = 0.0001249
alternative hypothesis: true difference in means between group placebo and group transplant is not equal to 0
95 percent confidence interval:
 -31.62100 -12.87163
sample estimates:
   mean in group placebo mean in group transplant 
                44.15496                 66.40127 

Conclusion:

We can conclude that the relative abundance of Staphylococcus in the microbiome of individuals with smelly armpits is 22.2% higher upon the transplant than upon the placebo treatment (p < 0.001).


2.4 Some concepts

What are the consequences of using a sample and randomisation?

  • Random sampling is closely related to the concept of the population or the scope of the study.

  • Based on a sample of subjects, the researchers want to come to conclusions that hold for

    • all kinds of people
    • only male students
  • Scope of the study should be well specified before the start of the study.

  • For the statistical analysis to be valid, it is required that the subjects are selected completely at random from the population to which we want to generalize our conclusions.

  • Selecting completely at random from a population implies:

    • all subjects in the population should have the same probability of being selected in the sample,
    • the selection of a subject in the sample should be independent from the selection of the other subjects in the sample.
  • The sample is thus supposed to be representative for the population, but still it is random.

  • What does this imply?


3 Sample to sample variability

National Health NHanes study

  • Since 1960 individuals of all ages are interviewed in their homes every year
  • The health examination component of the survey is conducted in a mobile examination centre (MEC).
  • We will use this large study to select random subjects from the American population.
  • This will help us to understand how the results of an analysis and the conclusions vary from sample to sample.

library(NHANES)
head(NHANES)
glimpse(NHANES)
Rows: 10,000
Columns: 76
$ ID               <int> 51624, 51624, 51624, 51625, 51630, 51638, 51646, 5164…
$ SurveyYr         <fct> 2009_10, 2009_10, 2009_10, 2009_10, 2009_10, 2009_10,…
$ Gender           <fct> male, male, male, male, female, male, male, female, f…
$ Age              <int> 34, 34, 34, 4, 49, 9, 8, 45, 45, 45, 66, 58, 54, 10, …
$ AgeDecade        <fct>  30-39,  30-39,  30-39,  0-9,  40-49,  0-9,  0-9,  40…
$ AgeMonths        <int> 409, 409, 409, 49, 596, 115, 101, 541, 541, 541, 795,…
$ Race1            <fct> White, White, White, Other, White, White, White, Whit…
$ Race3            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ Education        <fct> High School, High School, High School, NA, Some Colle…
$ MaritalStatus    <fct> Married, Married, Married, NA, LivePartner, NA, NA, M…
$ HHIncome         <fct> 25000-34999, 25000-34999, 25000-34999, 20000-24999, 3…
$ HHIncomeMid      <int> 30000, 30000, 30000, 22500, 40000, 87500, 60000, 8750…
$ Poverty          <dbl> 1.36, 1.36, 1.36, 1.07, 1.91, 1.84, 2.33, 5.00, 5.00,…
$ HomeRooms        <int> 6, 6, 6, 9, 5, 6, 7, 6, 6, 6, 5, 10, 6, 10, 10, 4, 3,…
$ HomeOwn          <fct> Own, Own, Own, Own, Rent, Rent, Own, Own, Own, Own, O…
$ Work             <fct> NotWorking, NotWorking, NotWorking, NA, NotWorking, N…
$ Weight           <dbl> 87.4, 87.4, 87.4, 17.0, 86.7, 29.8, 35.2, 75.7, 75.7,…
$ Length           <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ HeadCirc         <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ Height           <dbl> 164.7, 164.7, 164.7, 105.4, 168.4, 133.1, 130.6, 166.…
$ BMI              <dbl> 32.22, 32.22, 32.22, 15.30, 30.57, 16.82, 20.64, 27.2…
$ BMICatUnder20yrs <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ BMI_WHO          <fct> 30.0_plus, 30.0_plus, 30.0_plus, 12.0_18.5, 30.0_plus…
$ Pulse            <int> 70, 70, 70, NA, 86, 82, 72, 62, 62, 62, 60, 62, 76, 8…
$ BPSysAve         <int> 113, 113, 113, NA, 112, 86, 107, 118, 118, 118, 111, …
$ BPDiaAve         <int> 85, 85, 85, NA, 75, 47, 37, 64, 64, 64, 63, 74, 85, 6…
$ BPSys1           <int> 114, 114, 114, NA, 118, 84, 114, 106, 106, 106, 124, …
$ BPDia1           <int> 88, 88, 88, NA, 82, 50, 46, 62, 62, 62, 64, 76, 86, 6…
$ BPSys2           <int> 114, 114, 114, NA, 108, 84, 108, 118, 118, 118, 108, …
$ BPDia2           <int> 88, 88, 88, NA, 74, 50, 36, 68, 68, 68, 62, 72, 88, 6…
$ BPSys3           <int> 112, 112, 112, NA, 116, 88, 106, 118, 118, 118, 114, …
$ BPDia3           <int> 82, 82, 82, NA, 76, 44, 38, 60, 60, 60, 64, 76, 82, 7…
$ Testosterone     <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ DirectChol       <dbl> 1.29, 1.29, 1.29, NA, 1.16, 1.34, 1.55, 2.12, 2.12, 2…
$ TotChol          <dbl> 3.49, 3.49, 3.49, NA, 6.70, 4.86, 4.09, 5.82, 5.82, 5…
$ UrineVol1        <int> 352, 352, 352, NA, 77, 123, 238, 106, 106, 106, 113, …
$ UrineFlow1       <dbl> NA, NA, NA, NA, 0.094, 1.538, 1.322, 1.116, 1.116, 1.…
$ UrineVol2        <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ UrineFlow2       <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ Diabetes         <fct> No, No, No, No, No, No, No, No, No, No, No, No, No, N…
$ DiabetesAge      <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ HealthGen        <fct> Good, Good, Good, NA, Good, NA, NA, Vgood, Vgood, Vgo…
$ DaysPhysHlthBad  <int> 0, 0, 0, NA, 0, NA, NA, 0, 0, 0, 10, 0, 4, NA, NA, 0,…
$ DaysMentHlthBad  <int> 15, 15, 15, NA, 10, NA, NA, 3, 3, 3, 0, 0, 0, NA, NA,…
$ LittleInterest   <fct> Most, Most, Most, NA, Several, NA, NA, None, None, No…
$ Depressed        <fct> Several, Several, Several, NA, Several, NA, NA, None,…
$ nPregnancies     <int> NA, NA, NA, NA, 2, NA, NA, 1, 1, 1, NA, NA, NA, NA, N…
$ nBabies          <int> NA, NA, NA, NA, 2, NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ Age1stBaby       <int> NA, NA, NA, NA, 27, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ SleepHrsNight    <int> 4, 4, 4, NA, 8, NA, NA, 8, 8, 8, 7, 5, 4, NA, 5, 7, N…
$ SleepTrouble     <fct> Yes, Yes, Yes, NA, Yes, NA, NA, No, No, No, No, No, Y…
$ PhysActive       <fct> No, No, No, NA, No, NA, NA, Yes, Yes, Yes, Yes, Yes, …
$ PhysActiveDays   <int> NA, NA, NA, NA, NA, NA, NA, 5, 5, 5, 7, 5, 1, NA, 2, …
$ TVHrsDay         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ CompHrsDay       <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ TVHrsDayChild    <int> NA, NA, NA, 4, NA, 5, 1, NA, NA, NA, NA, NA, NA, 4, N…
$ CompHrsDayChild  <int> NA, NA, NA, 1, NA, 0, 6, NA, NA, NA, NA, NA, NA, 3, N…
$ Alcohol12PlusYr  <fct> Yes, Yes, Yes, NA, Yes, NA, NA, Yes, Yes, Yes, Yes, Y…
$ AlcoholDay       <int> NA, NA, NA, NA, 2, NA, NA, 3, 3, 3, 1, 2, 6, NA, NA, …
$ AlcoholYear      <int> 0, 0, 0, NA, 20, NA, NA, 52, 52, 52, 100, 104, 364, N…
$ SmokeNow         <fct> No, No, No, NA, Yes, NA, NA, NA, NA, NA, No, NA, NA, …
$ Smoke100         <fct> Yes, Yes, Yes, NA, Yes, NA, NA, No, No, No, Yes, No, …
$ Smoke100n        <fct> Smoker, Smoker, Smoker, NA, Smoker, NA, NA, Non-Smoke…
$ SmokeAge         <int> 18, 18, 18, NA, 38, NA, NA, NA, NA, NA, 13, NA, NA, N…
$ Marijuana        <fct> Yes, Yes, Yes, NA, Yes, NA, NA, Yes, Yes, Yes, NA, Ye…
$ AgeFirstMarij    <int> 17, 17, 17, NA, 18, NA, NA, 13, 13, 13, NA, 19, 15, N…
$ RegularMarij     <fct> No, No, No, NA, No, NA, NA, No, No, No, NA, Yes, Yes,…
$ AgeRegMarij      <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 20, 15, N…
$ HardDrugs        <fct> Yes, Yes, Yes, NA, Yes, NA, NA, No, No, No, No, Yes, …
$ SexEver          <fct> Yes, Yes, Yes, NA, Yes, NA, NA, Yes, Yes, Yes, Yes, Y…
$ SexAge           <int> 16, 16, 16, NA, 12, NA, NA, 13, 13, 13, 17, 22, 12, N…
$ SexNumPartnLife  <int> 8, 8, 8, NA, 10, NA, NA, 20, 20, 20, 15, 7, 100, NA, …
$ SexNumPartYear   <int> 1, 1, 1, NA, 1, NA, NA, 0, 0, 0, NA, 1, 1, NA, NA, 1,…
$ SameSex          <fct> No, No, No, NA, Yes, NA, NA, Yes, Yes, Yes, No, No, N…
$ SexOrientation   <fct> Heterosexual, Heterosexual, Heterosexual, NA, Heteros…
$ PregnantNow      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…

3.1 Data exploration

Suppose that we are interested in assessing the difference in direct cholesterol levels between males and females older than 25 years.

  1. We pipe the dataset to the function filter to filter the data according to age.
  2. We plot the direct cholesterol levels.
    • We select the data with the command ggplot(aes(x=DirectChol))
    • We add a histogram with the command geom_histogram()
    • We make to vertical panels using the command facet_grid(Gender~.)
    • We customize the label of the x-axis with the xlab command.
NHANES%>%filter(Age>25)%>%
  ggplot(aes(x=DirectChol))+
  geom_histogram() +
  facet_grid(Gender~.) +
  xlab("Direct cholesterol (mg/dl)")


  • Cholesterol levels and concentration measurements are often skewed.
  • Concentrations cannot be lower than 0.
  • They are often log transformed.
NHANES%>%
  filter(Age>25)%>%
  ggplot(aes(x=DirectChol%>%log2))+
  geom_histogram() +
  facet_grid(Gender~.) +
  xlab("Direct cholesterol (log2)")

We see that the data are more or less bell shaped upon log transformation.


We will now create a subset of the data that we will use to sample from in the next sections.

  1. We filter on age and remove subjects with missing values (NA).
  2. We only select the variables Gender and DirectChol from the dataset to avoid unnecessary variables.
  3. With the mutate function we can add a new variable logChol with log transformed direct cholesterol levels.
nhanesSub<- NHANES%>%
  filter(Age>25&!is.na(DirectChol)) %>%
  select(c("Gender","DirectChol")) %>%
  mutate(cholLog=log2(DirectChol))

We will calculate the summary statistics for the cholLog variable for males and females in the large dataset. So we group by Gender

cholLogSum<- nhanesSub %>% 
  group_by(Gender) %>%
   summarize_at("cholLog",
               list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))

cholLogSum

3.2 Experiment

  • Suppose that we have no access to cholesterol levels of the American population,
  • we will have to setup an experiment.
  • Suppose we have a budget for assessing 10 females and 10 males,
  • we will subset 10 females and 10 males at random from the American population and measure their direct cholesterol levels.
fem<-nhanesSub%>%filter(Gender=="female")%>%sample_n(size=10)
mal<-nhanesSub%>%filter(Gender=="male")%>%sample_n(size=10)

samp<-rbind(fem,mal)
samp

We will now plot the data with a histogram and boxplots

samp %>%
  ggplot(aes(x=cholLog))+
  geom_histogram(binwidth = .1) +
  facet_grid(Gender~.) +
  xlab("Direct cholesterol (log2)")

samp%>% 
  ggplot(aes(x=Gender,y=cholLog)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(position="jitter")


We summarize the data

samp %>% 
  group_by(Gender) %>%
   summarize_at("cholLog",
               list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))

Note that the sample mean is different from that of the large experiment (“population”) we sampled from.

We test for the difference between Males and females

t.test(cholLog~Gender,samp,var.equal=TRUE)

    Two Sample t-test

data:  cholLog by Gender
t = 1.8919, df = 18, p-value = 0.0747
alternative hypothesis: true difference in means between group female and group male is not equal to 0
95 percent confidence interval:
 -0.03588746  0.68570738
sample estimates:
mean in group female   mean in group male 
          0.34776170           0.02285174 

3.3 Repeat the experiment

If we do the experiment again we select other people and we obtain different results.

fem<-nhanesSub%>%filter(Gender=="female")%>%sample_n(size=10)
mal<-nhanesSub%>%filter(Gender=="male")%>%sample_n(size=10)

samp2<-rbind(fem,mal)
samp2%>%
  ggplot(aes(x=DirectChol%>%log))+
  geom_histogram(binwidth = .1) +
  facet_grid(Gender~.) +
  xlab("Direct cholesterol (log)")

samp2%>% 
  ggplot(aes(x=Gender,y=cholLog)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(position="jitter")

samp2 %>% 
  group_by(Gender) %>%
   summarize_at("cholLog",
               list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))
t.test(cholLog~Gender,samp2,var.equal=TRUE)

    Two Sample t-test

data:  cholLog by Gender
t = 1.7522, df = 18, p-value = 0.09675
alternative hypothesis: true difference in means between group female and group male is not equal to 0
95 percent confidence interval:
 -0.07604904  0.84039821
sample estimates:
mean in group female   mean in group male 
           0.6420087            0.2598341 

3.4 And again

set.seed(12857)
fem<-nhanesSub%>%filter(Gender=="female")%>%sample_n(size=10)
mal<-nhanesSub%>%filter(Gender=="male")%>%sample_n(size=10)

samp3<-rbind(fem,mal)
samp3%>%
  ggplot(aes(x=DirectChol%>%log))+
  geom_histogram(binwidth = .1) +
  facet_grid(Gender~.) +
  xlab("Direct cholesterol (log)")

samp3%>% 
  ggplot(aes(x=Gender,y=cholLog)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(position="jitter")

samp3 %>% 
  group_by(Gender) %>%
   summarize_at("cholLog",
               list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))
t.test(cholLog~Gender,samp3,var.equal=TRUE)

    Two Sample t-test

data:  cholLog by Gender
t = -2.4449, df = 18, p-value = 0.02501
alternative hypothesis: true difference in means between group female and group male is not equal to 0
95 percent confidence interval:
 -0.7049891 -0.0533427
sample estimates:
mean in group female   mean in group male 
           0.2585913            0.6377572 

3.5 Summary

  • Because we sampled other subjects in each sample, we obtain different cholesterol levels.

  • However, not only the cholesterol levels differ from sample to sample but also the summary statistics: means, standard deviations and standard errors.

  • Note, that in the last sample the log cholesterol levels are on average lower for females than for males; based on this sample we even would wrongly conclude that the cholesterol levels for females are on average larger than those of males.

  • This implies that our conclusions are also subjected to uncertainty and might change from sample to sample.

  • Samples as the one where the effect swaps and is statistically significant, however, are very rare.

  • This is illustrated with the code below, where we will draw 20000 repeated samples with sample size 10 for females and males from the NHanes study.

nsim<-20000
nSamp<-10
res<-matrix(0,nrow=nsim,ncol=2)
fem<-nhanesSub%>%filter(Gender=="female")
mal<-nhanesSub%>%filter(Gender=="male")

for (i in 1:nsim)
{
 femSamp<-sample(fem$cholLog,nSamp)
 malSamp<-sample(mal$cholLog,nSamp)

meanFem<-mean(femSamp)
 meanMal<-mean(malSamp)
 delta<-meanFem-meanMal
 sdFem<-sd(femSamp)
 sdMal<-sd(malSamp)
 seFem<-sdFem/sqrt(nSamp)
 seFem<-sdFem/sqrt(nSamp)
 sdPool<-sqrt((sdFem^2*(nSamp-1) + sdMal^2*(nSamp-1))/(2*nSamp-2))
tvalue<-(delta)/(sdPool*sqrt(1/nSamp+1/nSamp))
pvalue<-pt(abs(tvalue),lower.tail = FALSE,df=2*nSamp-2)*2
 res[i,]<-c(delta,pvalue)
}
sum(res[,2]<0.05&res[,1]>0)
[1] 7785
sum(res[,2]>0.05)
[1] 12212
sum(res[,2]<0.05&res[,1]<0)
[1] 3
res<-res %>% as.data.frame
names(res) <- c("delta","pvalue")
res %>% 
  ggplot(aes(x=delta,y=-log10(pvalue),color=pvalue<0.05)) + 
  geom_point() + 
  xlab("Average cholesterol difference") +
  ylab("- log10(pvalue)") +
  scale_color_manual(values=c("black","red"))

res %>% 
  ggplot(aes(y=delta)) +
  geom_boxplot() +
  geom_point(aes(x=0,y=c(mean(fem$cholLog)-mean(mal$cholLog)),color="pop. diff")) +
  xlab("")

Only in 3 out of 20000 samples we conclude that the mean cholesterol level of males is significantly lower than for females. For the remaining samples the cholesterol levels for males were on average significantly lower than for females (7785 samples) or the average difference in cholesterol levels were not statistically significant (12212 samples). The latter is because the power is rather low to detect the difference with 10 samples in each group.


3.6 Assignment

  1. Copy the code chunk with the simulation study
  2. Add it here below
  3. Modify the sample size to 50.
  4. What do you observe?

3.7 Control of false positives

Wat happens when there is no difference between both groups?

  • We will have to simulate experiments for which the cholestorol levels are the same for both groups.

  • We can do this by sampling data for both groups from the subset of women in the study.

  • We do this again for 10 subjects per group

nsim<-20000
nSamp<-10
res<-matrix(0,nrow=nsim,ncol=2)
fem<-nhanesSub%>%filter(Gender=="female")
mal<-nhanesSub%>%filter(Gender=="male")

for (i in 1:nsim)
{
 femSamp<-sample(fem$cholLog,nSamp)
 fem2Samp<-sample(fem$cholLog,nSamp)

meanFem<-mean(femSamp)
 meanFem2<-mean(fem2Samp)
 delta<-meanFem-meanFem2
 sdFem<-sd(femSamp)
 sdFem2<-sd(fem2Samp)
 seFem<-sdFem/sqrt(nSamp)
 seFem<-sdFem2/sqrt(nSamp)
 sdPool<-sqrt((sdFem^2*(nSamp-1) + sdFem2^2*(nSamp-1))/(2*nSamp-2))
tvalue<-(delta)/(sdPool*sqrt(1/nSamp+1/nSamp))
pvalue<-pt(abs(tvalue),lower.tail = FALSE,df=2*nSamp-2)*2
 res[i,]<-c(delta,pvalue)
}
sum(res[,2]<0.05&res[,1]>0)
[1] 480
sum(res[,2]>=0.05)
[1] 18960
sum(res[,2]<0.05&res[,1]<0)
[1] 560
res<-res %>% as.data.frame
names(res) <- c("delta","pvalue")
res %>% 
  ggplot(aes(x=delta,y=-log10(pvalue),color=pvalue<0.05)) + 
  geom_point() + 
  xlab("Average cholesterol difference") +
  ylab("- log10(pvalue)") +
  scale_color_manual(values=c("black","red"))

res %>% 
  ggplot(aes(y=delta)) +
  geom_boxplot() +
  geom_point(aes(x=0,y=0,color="pop. diff")) +
  xlab("")

Note, that the number of false positives are on 1040 on 20000 experiments and are nicely controlled at the 5% level.

What happens if we increase the sample size to 50 subjects per group?


4 Salk Study

  • In 1916, the US experienced the first large epidemic of polio.
  • John Salk developed a vaccine with promising results in the lab in the early fifties.
  • In 1954, the National Foundation for Infantile Paralysis (NFIP) has setup a large study to assess the effectiveness of the Salk vaccine.
  • Suppose that the NFIP would have vaccinated a large number of children in 1954 and would have observed that the polio incidence in 1954 was lower than in 1953. Could they have concluded that the vaccine was effective?

4.1 NFIP Study

4.1.1 Design

  • Large simultaneous study with cases, vaccinated children, and controls, non-vaccinated children.
  • All schools in districts with high polio incidence
  • Cases: children with consent for vaccination from second grade of primary school.
  • Controls: children from first and third grade.

4.1.2 Data

nfip<-data.frame(group=c("cases","control","noConcent"),grade=c("g2","g1g3","g2"),vaccin=c("yes","no","no"),total=c(221998,725173,123605),polio=c(54,391,56))
nfip$noPolio<-nfip$total-nfip$polio
knitr::kable(nfip)
group grade vaccin total polio noPolio
cases g2 yes 221998 54 221944
control g1g3 no 725173 391 724782
noConcent g2 no 123605 56 123549

Compare polio incidence?

What can we conclude?


4.2 Confounding

  • We observe a lower polio (P) incidence for children for who no consent was given than for the children in the control group.

  • Consent for vaccination (V) was associated with the socio-economic status (S).

  • Children of lower socio-economic status were more resistant to the disease.

  • The groups of cases and controls are not comparable:

    • difference in age,
    • difference in socio-economic status and
    • difference in susceptible for disease.

4.3 Salk Study

4.3.1 Design

A new study was conducted: Randomized double blind study

  • Children are assigned at random to the control or case treatment arm after consent was given by the parents.
  • Control: vaccination with placebo
  • Treatment: vaccination with vaccine
  • double blinding:
    • parents did not know if their child was vaccinated or received the placebo
    • care-giver/researchers did not know if the child was vaccinated or received placebo

4.3.2 Data

salk<-data.frame(group=c("cases","control","noConcent"),treatment=c("vaccine","placebo","none"),total=c(200745,
201229, 338778),polio=c(57,142,157))
salk$noPolio<-salk$total-salk$polio
salk$incidencePM<-round(salk$polio/salk$total*1e6,0)
knitr::kable(salk)
group treatment total polio noPolio incidencePM
cases vaccine 200745 57 200688 284
control placebo 201229 142 201087 706
noConcent none 338778 157 338621 463
  • We observe a much larger effect now that the cases and the controls are comparable, incidence of 284 and 706 per million, respectively.

  • The polio incidence for children with no consent remains similar, and 463 per million in the NFIP and Salk study, respectively.


5 Role of Statistics in the Life Sciences

  • We have seen that
    • it is important to carefully specify the scope of the study before the experiment,
    • the sample size matters,
    • we should be aware of confounding, and
    • a proper control is required.

\(\rightarrow\) Good experimental design is crucial!

  • We also observed that there is variability in the population and because we can only sample a small part of the population our results and conclusions are subjected to uncertainty.

  • Statistics is the science on

    1. collecting (experimental design),
    2. exploring (data exploration) and
    3. learning from data and to generalize what we observe in the sample towards the population while quantifying, controlling and reporting variability and uncertainty (statistical modelling and statistical inference).
  • Therefore, statistics plays an important role in almost all sciences (e.g. column “points of significance” in Nature Methods. http://blogs.nature.com/methagora/2013/08/giving_statistics_the_attention_it_deserves.html)


LS0tCnRpdGxlOiAiMS4gSW50cm9kdWN0aW9uOiBXaHkgZG8gd2UgbmVlZCBzdGF0aXN0aWNzIiAKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCm91dHB1dDoKICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgICAgCiAgICAgIHRoZW1lOiBjb3NtbwogICAgICB0b2M6IHRydWUKICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChpbmNsdWRlID0gVFJVRSwgY29tbWVudCA9IE5BLCBlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIE1vdGl2YXRpb24gCjxhIHJlbD0ibGljZW5zZSIgaHJlZj0iaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLXNhLzQuMCI+PGltZyBhbHQ9IkNyZWF0aXZlIENvbW1vbnMgTGljZW5zZSIgc3R5bGU9ImJvcmRlci13aWR0aDowIiBzcmM9Imh0dHBzOi8vaS5jcmVhdGl2ZWNvbW1vbnMub3JnL2wvYnktbmMtc2EvNC4wLzg4eDMxLnBuZyIgLz48L2E+CgohW10oaHR0cHM6Ly9pbWFnZXMubWFydGVjaGFkdmlzb3IuY29tL2ltYWdlcy91cGxvYWRzL2NvbnRlbnRfaW1hZ2VzL3dheXNfdG9fY2FwdHVyZV9iMmJfY3VzdG9tZXJfZGF0YV81YmFjZDJkYmUxYTA5LnBuZykKCiFbXSguL2ZpZ3VyZXMvd3BHcmFwaC5qcGVnKQoKLSBXZSBsaXZlIGluIGEgYmlnIGRhdGEgZXJhCi0gRGF0YSBvbiBsb2NhdGlvbiwgY2xpY2tzLCBlLWNvbW1lcmNlLCBzb2NpYWwgbWVkaWEgLi4uCi0gTGlmZSBTY2llbmNlczogbWVhc3VyZSBleHByZXNzaW9uIG9mIHRob3VzYW5kcyBvZiBnZW5lcywgcHJvdGVpbnMsIC4uLiBmb3IgZWFjaCBzdWJqZWN0IG9yIGV2ZW4gaW5kaXZpZHVhbCBjZWxscyAKLSBEYXRhIGRyaXZlbiBqb3VybmFsaXNtCi0gLi4uCgpTdGF0aXN0aWNzIGlzIHRoZSBzY2llbmNlIHRvIGxlYXJuIGZyb20gZW1waXJpY2FsIGRhdGEuIAoKU3RhdGlzdGljYWwgbGl0ZXJhY3kgaXMga2V5IHRvIGludGVycHJldCByZXN1bHRzIGZyb20gc2NpZW50aWZpYyBwdWJsaWNhdGlvbnMuCgoKCiMjIFRoZXJlIGFyZSB0aHJlZSB0eXBlcyBvZiBsaWVzOiBsaWVzLCBkYW1uIGxpZXMgYW5kIHN0YXRpc3RpY3Mgey19CgotLS0KCiMjIFRoZXJlIGFyZSB0aHJlZSB0eXBlcyBvZiBsaWVzOiBsaWVzLCBkYW1uIGxpZXMgYW5kIGJhZCBzdGF0aXN0aWNzIHstfQoKLS0tCgpodHRwczovL3d3dy5tZWRyeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvMjAyMC4wNy4xNy4yMDE1NTg0NnYxCgohW10oaHR0cHM6Ly9naXRodWIuY29tL3N0YXRPbWljcy9zYmMyMC9yYXcvbWFzdGVyL2ZpZ3VyZXMvRm9uc2VjYTIwMjBDb3ZpZFZlZ2V0YWJsZXMucG5nKQoKLS0tCgohW10oaHR0cHM6Ly9naXRodWIuY29tL3N0YXRPbWljcy9zYmMyMC9yYXcvbWFzdGVyL2ZpZ3VyZXMvY292aWRWZWdldGFibGVzNi5wbmcpe3dpZHRoPTcwJX0KCgotLS0KCiFbXShodHRwczovL2dpdGh1Yi5jb20vc3RhdE9taWNzL3NiYzIwL3Jhdy9tYXN0ZXIvZmlndXJlcy9jb3ZpZFZlZ2V0YWJsZXMucG5nKXt3aWR0aD03MCV9CgoiVGhlIGF1dGhvcnMgc3RhdGU6IFRoZSBuZWdhdGl2ZSBlY29sb2dpY2FsIGFzc29jaWF0aW9uIGJldHdlZW4gQ09WSUQtMTkgbW9ydGFsaXR5IGFuZCB0aGUgY29uc3VtcHRpb24gb2YgY2FiYmFnZSBhbmQgY3VjdW1iZXIgc3VwcG9ydHMgdGhlIGEgcHJpb3JpIGh5cG90aGVzaXMgcHJldmlvdXNseSByZXBvcnRlZC4KSW4gdGhpcyBoeXBvdGhlc2lzLCB3ZSBwcm9wb3NlZCB0aGF0IHZlZ2V0YWJsZXMgc3VjaCBhcyBCcmFzc2ljYSAtIHdpdGggYW4gYW50aW94aWRhbnQgYWN0aXZpdHkgcmVkdWNpbmcgaW5zdWxpbiByZXNpc3RhbmNlIC0gbWF5IGFsc28gYmUgYXNzb2NpYXRlZCB3aXRoIGxvdyBDT1ZJRC0xOSBtb3J0YWxpdHkgaW4gY291bnRyaWVzLgoiCgoiClRob3VnaCBvdXIgcmVzdWx0cyBkbyBub3QgYWxsb3cgdG8gaW5mZXIgY2F1c2FsaXR5LCB0aGV5IGRvIHJlaW5mb3JjZSBvdXIgYSBwcmlvcnkgaHlwb3RoZXNpcyB0aGF0IHRoZSBpbmdlc3Rpb24gb2YgYW50aS1veGlkYW50IGZvb2RzIGFjdGluZyBvbiBpbnN1bGluIGludG9sZXJhbmNlIG1heSBoYXZlIHJlZHVjZWQgdGhlIHNldmVyaXR5IG9mIENPVklELTE5LgoiCgotLS0KCiFbXShodHRwczovL2dpdGh1Yi5jb20vc3RhdE9taWNzL3NiYzIwL3Jhdy9tYXN0ZXIvZmlndXJlcy9jb3ZpZFZlZ2V0YWJsZXM0LnBuZykKCi0tLQoKIVtdKGh0dHBzOi8vZ2l0aHViLmNvbS9zdGF0T21pY3Mvc2JjMjAvcmF3L21hc3Rlci9maWd1cmVzL2NvdmlkVmVnZXRhYmxlczMucG5nKQoKLSBNYW55IGh5cG90aGVzZXMgYXJlIGFzc2Vzc2VkID8hCi0gQ2F1c2FsaXR5ID8hCi0gRXhwZXJpbWVudGFsIGRlc2lnbjogT2JzZXJ2YXRpb25hbCBzdHVkeQotIEJhc2VkIG9uIHRoZSBkYXRhIHdlIGNhbm5vdCBwcm92aWRlIHJlY29tbWVuZGF0aW9ucyBhdCB0aGUgc3ViamVjdCBsZXZlbAoKLS0tCgohW10oaHR0cHM6Ly9naXRodWIuY29tL3N0YXRPbWljcy9zYmMyMC9yYXcvbWFzdGVyL2ZpZ3VyZXMvY292aWRWZWdldGFibGVzMi5wbmcpe3dpZHRoPTcwJX0KCi0gSW1wb3J0YW5jZSBvZiBEYXRhIEV4cGxvcmF0aW9uIQotIERhdGEgZG9lcyBub3QgZXhoaWJpdCB0aGUgdHJlbmQKLSBEYXRhIHNob3dzIGV2aWRlbmNlIGZvciB0d28gY2x1c3RlcnM6IGFib3ZlIGFuZCBiZWxvdyAyMDAgZGVhdGhzL21pbGxpb24KLSBNb2RlbCBmb3IgY3VjdW1iZXIgZG9lcyBub3QgbW9kZWwgdGhlIGRhdGEgY29ycmVjdGx5OiBPdmVyZXN0aW1hdGlvbiBvZiBkZWF0aCByYXRlIGZvciBtYW55IGNvdW50cmllcyBlLmcuIFBvcnR1Z2FsLCBIdW5nYXJ5LCAuLi4KCi0tLQoKYGBge3IgZWNobz1GQUxTRSxvdXQud2lkdGg9IjEwJSIsb3V0LmV4dHJhPSdzdHlsZT0iZmxvYXQ6cmlnaHQ7IHBhZGRpbmc6MTBweCInfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9maWd1cmVzL2ZlYXIucG5nIikKYGBgCgpQYXBlcnMgYXJlIG1lcmdlZCwgcHVibGlzaGVkLCBhbmQgMzkgdGltZXMgY2l0ZWQgCgpCb3VzcXVldCBldCBhbC4gKDIwMjEpLiBDYWJiYWdlIGFuZCBmZXJtZW50ZWQgdmVnZXRhYmxlczogRnJvbSBkZWF0aCByYXRlIGhldGVyb2dlbmVpdHkgaW4gY291bnRyaWVzIHRvIGNhbmRpZGF0ZXMgZm9yIG1pdGlnYXRpb24gc3RyYXRlZ2llcyBvZiBzZXZlcmUgQ09WSUQtMTkuIEFsbGVyZ3kgNzY6NzM14oCTNzUwLgoKLS0tCgpJbiB0aGUgZXhhbXBsZSB3ZSBjb3ZlcmVkIGlzc3VlcyB3aXRoIHRocmVlIGltcG9ydGFudCBicmFuY2hlcyBpbiBzdGF0aXN0aWNzCgoxLiBTdGF0aXN0aWNhbCBpbmZlcmVuY2UKCiAgLSBJcyB0aGVyZSBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuICBDT1ZJRC0xOSBtb3J0YWxpdHkgYW5kIGZvb2QgY29uc3VtcHRpb24/CiAgLSBJc3N1ZSBpbiBzdHVkeToKICAgICAgLSBkYXRhIGRyZWRnaW5nLCBwLWhhY2tpbmcsIC4uLjogd2hlbiB5b3UgYXNzZXNzIG1hbnkgaHlwb3RoZXNlcyB5b3Ugd2lsbCBhbGx3YXlzIGZpbmQgc3Ryb25nIHBhdHRlcm5zIGJ5IHJhbmRvbSBjaGFuZ2UgJFxyaWdodGFycm93JCBjb3JyZWN0IGZvciBtdWx0aXBsZSB0ZXN0aW5nISAKICAgICAgLSBBc3N1bXB0aW9ucyBvZiB0aGUgbW9kZWxzIGRvIG5vdCBob2xkCiAgICAgIC0gQ29uZm91bmRpbmcKCjIuIEV4cGVyaW1lbnRhbCBkZXNpZ24KCiAgICAtIENvbmZvdW5kaW5nOiBjb3VudHJpZXMgZG8gbm90IG9ubHkgZGlmZmVyIGluIGNvbnN1bXB0aW9uIG9mIGEgdmVnZXRhYmxlIGJ1dCBhbHNvIGluIG1heSBvdGhlciB2YXJpYWJsZXMgKGRlbW9ncmFwaGljYWwsIENPVklEIG1lYXN1cmVzLCBoZWFsdHkgY2FyZSwgLi4uKSB0aGF0IGFyZSBhc3NvY2lhdGVkIHdpdGggQ09WSUQgbW9ydGFsaXR5LiAKCiAgICAtIERpZmZpY3VsdCB0byBkcmF3IGNhdXNhbCBjb25jbHVzaW9ucyBmcm9tIG9ic2VydmF0aW9uYWwgc3R1ZGllcy4gCiAgICAKICAgIC0gSW4gZXhwZXJpbWVudGFsIHN0dWRpZXM6IHJhbmRvbWlzYXRpb24hICRccmlnaHRhcnJvdyQgc28gdGhhdCAgdGhlIGdyb3VwcyBvbmx5IGRpZmZlciBpbiB0aGUgdHJlYXRtZW50LiAKICAgIAogICAgLSBFeHBlcmltZW50YWwgc3R1ZGllcyBhcmUgdGhlcmVmb3JlIHRoZSBnb2xkZW4gc3RhbmRhcmQKCjMuIERhdGEgZXhwbG9yYXRpb24gYW5kIHZpc3VhbGlzYXRpb24KCiAgICAtIENydWNpYWwgdG8gZ2V0IGluc2lnaHQgaW4gdGhlIGRhdGEhCiAgICAtIEFzc2VzcyBtb2RlbCBhc3N1bXB0aW9ucwoKLS0tCgojIFNtZWxseSBhcm1waXQgZXhhbXBsZQoKLSBTbWVsbHkgYXJtcGl0cyBhcmUgbm90IGNhdXNlZCBieSBzd2VhdCBpdHNlbGYuIFRoZSBzbWVsbCBpcyBjYXVzZWQgYnkgc3BlY2lmaWMgbWljcm8tb3JnYW5pc21zIGJlbG9uZ2luZyB0byB0aGUgZ3JvdXAgb2YgKkNvcnluZWJhY3Rlcml1bSBzcHAuKiB0aGF0IG1ldGFib2xpc2Ugc3dlYXQuCkFub3RoZXIgZ3JvdXAgb2YgYWJ1bmRhbnQgYmFjdGVyaWEgYXJlIHRoZSAqU3RhcGh5bG9jb2NjdXMgc3BwLiosIHRoZXNlIGJhY3RlcmlhIGRvIG5vdCBtZXRhYm9saXNlIHN3ZWF0IGluIHNtZWxseSBjb21wb3VuZHMuCgotIFRoZSBDTUVULWdyb2VwIGF0IEdoZW50IFVuaXZlcnNpdHkgZG9lcyByZXNlYXJjaCBvbiB0cmFuc3BsYW50aW5nIHRoZSBhcm1waXQgbWljcm9iaW9tZSB0byBzYXZlIHBlb3BsZSB3aXRoIHNtZWxseSBhcm1waXRzLgoKLSBQcm9wb3NlZCBUaGVyYXB5OgogIAkxLiBSZW1vdmUgYXJtcGl0LW1pY3JvYmlvbWUgd2l0aCBhbnRpYmlvdGljcwogICAgMi4gSW5mbHVlbmNlIGFybXBpdCBtaWNyb2Jpb21lIHdpdGggbWljcm9iaWFsICB0cmFuc3BsYW50IChodHRwczovL3lvdXR1LmJlLzlSSUZ5cUxYZFZ3KQoKCmBgYHtyIG91dC53aWR0aD0nODAlJyxmaWcuYXNwPS44LCBmaWcuYWxpZ249J2NlbnRlcicsZWNobz1GQUxTRX0KaWYgKCJwaSIlaW4lbHMoKSkgcm0oInBpIikKa29wdm9ldGVyPC1mdW5jdGlvbih4LHksYW5nbGU9MCxsPS4yLGNleC5kb3Q9LjUscGNoPTE5LGNvbD0iYmxhY2siKQp7CmFuZ2xlPWFuZ2xlLzE4MCpwaQpwb2ludHMoeCx5LGNleD1jZXguZG90LHBjaD1wY2gsY29sPWNvbCkKbGluZXMoYyh4LHgrbCpjb3MoLXBpLzIrYW5nbGUpKSxjKHkseStsKnNpbigtcGkvMithbmdsZSkpLGNvbD1jb2wpCmxpbmVzKGMoeCtsLzIqY29zKC1waS8yK2FuZ2xlKSx4K2wvMipjb3MoLXBpLzIrYW5nbGUpK2wvNCpjb3MoYW5nbGUpKSxjKHkrbC8yKnNpbigtcGkvMithbmdsZSkseStsLzIqc2luKC1waS8yK2FuZ2xlKStsLzQqc2luKGFuZ2xlKSksY29sPWNvbCkKbGluZXMoYyh4K2wvMipjb3MoLXBpLzIrYW5nbGUpLHgrbC8yKmNvcygtcGkvMithbmdsZSkrbC80KmNvcyhwaSthbmdsZSkpLGMoeStsLzIqc2luKC1waS8yK2FuZ2xlKSx5K2wvMipzaW4oLXBpLzIrYW5nbGUpK2wvNCpzaW4ocGkrYW5nbGUpKSxjb2w9Y29sKQpsaW5lcyhjKHgrbCpjb3MoLXBpLzIrYW5nbGUpLHgrbCpjb3MoLXBpLzIrYW5nbGUpK2wvMipjb3MoLXBpLzIrcGkvNCthbmdsZSkpLGMoeStsKnNpbigtcGkvMithbmdsZSkseStsKnNpbigtcGkvMithbmdsZSkrbC8yKnNpbigtcGkvMitwaS80K2FuZ2xlKSksY29sPWNvbCkKbGluZXMoYyh4K2wqY29zKC1waS8yK2FuZ2xlKSx4K2wqY29zKC1waS8yK2FuZ2xlKStsLzIqY29zKC1waS8yLXBpLzQrYW5nbGUpKSxjKHkrbCpzaW4oLXBpLzIrYW5nbGUpLHkrbCpzaW4oLXBpLzIrYW5nbGUpK2wvMipzaW4oLXBpLzItcGkvNCthbmdsZSkpLGNvbD1jb2wpCn0KCnBhcihtYXI9YygwLDAsMCwwKSxtYWk9YygwLDAsMCwwKSkKcGxvdCgwLDAseGxhYj0iIix5bGFiPSIiLHhsaW09YygwLDEwKSx5bGltPWMoMCwxMCksY29sPTAseGF4dD0ibm9uZSIseWF4dD0ibm9uZSIsYXhlcz1GQUxTRSkKcmVjdCgwLDYsMTAsMTAsYm9yZGVyPSJyZWQiLGx3ZD0yKQp0ZXh0KC41LDgsInBvcHVsYXRpb24iLHNydD05MCxjb2w9InJlZCIsY2V4PTIpCnN5bWJvbHMgKDMsIDgsIGNpcmNsZXM9MS41LCBjb2w9InJlZCIsYWRkPVRSVUUsZmc9InJlZCIsaW5jaGVzPUZBTFNFLGx3ZD0yKQpzZXQuc2VlZCgzMzApCmdyaWQ9c2VxKDAsMS4zLC4wMSkKCmZvciAoaSBpbiAxOjUwKQp7CglhbmdsZTE9cnVuaWYobj0xLG1pbj0wLG1heD0zNjApCglhbmdsZTI9cnVuaWYobj0xLG1pbj0wLG1heD0zNjApCglyYWRpdXM9c2FtcGxlKGdyaWQscHJvYj1ncmlkXjIqcGkvc3VtKGdyaWReMipwaSksc2l6ZT0xKQoJa29wdm9ldGVyKDMrcmFkaXVzKmNvcyhhbmdsZTEvMTgwKnBpKSw4K3JhZGl1cypzaW4oYW5nbGUxLzE4MCpwaSksYW5nbGU9YW5nbGUyKQp9CnRleHQoNy41LDgsIk1pY3JvYmlvbWUgaW4gcG9wdWxhdGlvbiIsY29sPSJyZWQiLGNleD0xLjIpCgpyZWN0KDAsMCwxMCw0LGJvcmRlcj0iYmx1ZSIsbHdkPTIpCnRleHQoLjUsMiwic2FtcGxlIixzcnQ9OTAsY29sPSJibHVlIixjZXg9MikKc3ltYm9scyAoMywgMiwgY2lyY2xlcz0xLjUsIGNvbD0icmVkIixhZGQ9VFJVRSxmZz0iYmx1ZSIsaW5jaGVzPUZBTFNFLGx3ZD0yKQpmb3IgKGkgaW4gMDoxKQoJZm9yIChqIGluIDA6NCkKewoKCWtvcHZvZXRlcigyLjEraiooMy45LTIuMSkvNCwxLjEraS8yLGNvbD0icHVycGxlIikKfQpmb3IgKGkgaW4gMjozKQoJZm9yIChqIGluIDA6NCkKewoKCWtvcHZvZXRlcigyLjEraiooMy45LTIuMSkvNCwxLjYraS8yLGNvbD0ib3JhbmdlIikKfQp0ZXh0KDcuNSwyLCJNaWNyb2Jpb21lIGluIHNhbXBsZSIsY29sPSJibHVlIixjZXg9MS4yKQoKYXJyb3dzKDMsNS45LDMsNC4xLGNvbD0iYmxhY2siLGx3ZD0zKQp0ZXh0KDEuNSw1LCJFWFAuIERFU0lHTiAoMSkiLGNvbD0iYmxhY2siLGNleD0xLjIpCnRleHQoNy41LC41LCJEQVRBIEVYUExPUkFUSU9OICZcbkRFU0NSSVBUSVZFIFNUQVRJU1RJQ1MgKDIpIixjb2w9ImJsYWNrIixjZXg9MS4yKQphcnJvd3MoNyw0LjEsNyw1LjksY29sPSJibGFjayIsbHdkPTMpCnRleHQoOC41LDUsIkVTVElNQVRJT04gJlxuSU5GRVJFTkNFICgzKSIsY29sPSJibGFjayIsY2V4PTEuMikKYGBgCgoKLSBFeHBlcmltZW50OgoKICAgIC0gMjAgc3ViamVjdHMgd2l0aCBzbWVsbHkgYXJtcGl0cyBhcmUgYXR0cmlidXRlZCB0byBvbmUgb2YgdHdvIHRyZWF0bWVudCBncm91cHMKICAgIC0gcGxhY2VibyAob25seSBhbnRpYmlvdGljcykKICAgIC0gdHJhbnNwbGFudCAoYW50aWJpb3RpY3MgZm9sbG93ZWQgYnkgbWljcm9iaWFsIHRyYW5zcGxhbnQpLgogICAgLSBUaGUgbWljcm9iaW9tZSBpcyBzYW1wbGVkIDYgd2Vla3MgdXBvbiB0aGUgdHJlYXRtZW50LgogICAgLSBUaGUgcmVsYXRpdmUgYWJ1bmRhbmNlIG9mICpTdGFwaHlsb2NvY2N1cyBzcHAuKiBvbiAqQ29yeW5lYmFjdGVyaXVtIHNwcC4qICsgKlN0YXBoeWxvY29jY3VzIHNwcC4qIGluIHRoZSBtaWNyb2Jpb21lIGlzIG1lYXN1cmVkIHZpYSBER0dFICgqRGVuYXR1cmluZyBHcmFkaWVudCBHZWwgRWxlY3Ryb3Bob3Jlc2lzKikuCgotLS0KCiMjIEltcG9ydCB0aGUgZGF0YQpgYGB7cn0KcmVhZF9saW5lcygiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dUUEIvUFNMUzIwL21hc3Rlci9kYXRhL2FybXBpdC5jc3YiKQpgYGAKClRoZSBmaWxlIGlzIGNvbW1hIHNlcGFyYXRlZCBhbmQgaW4gdGlkeSBmb3JtYXQKCmBgYHtyfQphcDwtcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HVFBCL1BTTFMyMC9tYXN0ZXIvZGF0YS9hcm1waXQuY3N2IikKYXAKYGBgCgotLS0KCiMjIERhdGEgRXhwbG9yYXRpb24gYW5kIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MKCi0gRGF0YSBleHBsb3JhdGlvbiBpcyBleHRyZW1lbHkgaW1wb3J0YW50IHRvIGdldCBpbnNpZ2h0IGluIHRoZSBkYXRhLiAKLSBJdCBpcyBvZnRlbiB1bmRlcnJhdGVkIGFuZCBvdmVybG9va2VkLiAKCiMjIyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzCgpXZSBmaXJzdCBzdW1tYXJpemUgdGhlIGRhdGEgYW5kIGNhbGN1bGF0ZSB0aGUgbWVhbiwgc3RhbmRhcmQgZGV2aWF0aW9uLCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGFuZCBzdGFuZGFyZCBlcnJvciBhbmQgc3RvcmUgdGhlIHJlc3VsdCBpbiBhbiBvYmplY3QgYXBSZWxTdW0gdmlhICdhcFJlbFN1bTwtYAoKMS4gV2UgcGlwZSB0aGUgYGFwYCBkYXRhZnJhbWUgdG8gdGhlIGdyb3VwX2J5IGZ1bmN0aW9uIHRvIGdyb3VwIHRoZSBkYXRhIGJ5IHRyZWF0bWVudCB0cnQgYGdyb3VwX2J5KHRydClgCjIuIFdlIHBpcGUgdGhlIHJlc3VsdCB0byB0aGUgYHN1bW1hcml6ZV9hdGAgZnVuY3Rpb24gdG8gc3VtbWFyaXplIHRoZSAicmVsIiB2YXJpYWJsZSBhbmQgY2FsY3VsYXRlIHRoZSBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIAozLiBXZSBwaXBlIHRoZSByZXN1bHQgdG8gdGhlIGBtdXRhdGVgIGZ1bmN0aW9uIHRvIG1ha2UgYSBuZXcgdmFyaWFibGUgaW4gdGhlIGRhdGEgZnJhbWUgYHNlYCBmb3Igd2hpY2ggd2UgY2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBlcnJvciAKCgpgYGB7cn0KYXBSZWxTdW08LWFwJT4lCiAgZ3JvdXBfYnkodHJ0KSU+JQogIHN1bW1hcml6ZV9hdCgicmVsIiwKICAgICAgICAgICAgICAgbGlzdChtZWFuPX5tZWFuKC4sbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICAgc2Q9fnNkKC4sbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICAgbj1mdW5jdGlvbih4KSB4JT4laXMubmElPiVgIWAlPiVzdW0pKSAlPiUKICBtdXRhdGUoc2U9c2Qvc3FydChuKSkKCmFwUmVsU3VtCmBgYAoKLS0tCgojIyMgUGxvdHMKV2Ugd2lsbCB1c2UgZ2dwbG90MiB0byBtYWtlIG91ciBwbG90cy4gCldpdGggdGhlIGdncGxvdDIgbGlicmFyeSB3ZSBjYW4gZWFzaWx5IGJ1aWxkIHBsb3RzIGJ5IGFkZGluZyBsYXllcnMuCgojIyMjIGJhcnBsb3QKCjEuIFdlIHBpcGUgb3VyIHN1bW1hcml6ZWQgZGF0YSB0byB0aGUgYGdncGxvdGAgZnVuY3Rpb24gYW5kIHdlIHNlbGVjdCB0aGUgdHJlYXRtZW50IHZhcmlhYmxlIHRydCBhbmQgdGhlIHZhcmlhYmxlIG1lYW4gZm9yIHBsb3R0aW5nIGBhZXMoeD10cnQseT1tZWFuKWAKCjIuIFdlIG1ha2UgYSBiYXJwbG90IGJhc2VkIG9uIHRoaXMgZGF0YSB1c2luZyB0aGUgYGdlb21fYmFyYCBmdW5jdGlvbi4gVGhlIHN0YXRpc3RpYyBpcyBgc3RhdD0iaWRlbnRpdHkiYCBiZWNhdXNlIHRoZSBiYXIgaGVpZ2h0IHNob3VsZCBiZSBlcXVhbCB0aGUgdmFsdWUgZm9yIHRoZSBtZWFuIG9mIHRoZSByZWxhdGl2ZSBhYnVuZGFuY2UuCgpgYGB7cn0KYXBSZWxTdW0lPiUgCiAgZ2dwbG90KGFlcyh4PXRydCx5PW1lYW4pKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSAKYGBgCgotIElzIHRoaXMgcGxvdCBpbmZvcm1hdGl2ZT8/CgotLS0KCldlIHdpbGwgbm93IGFkZCBzdGFuZGFyZCBlcnJvcnMgdG8gdGhlIHBsb3QKdXNpbmcgYGdlb21fZXJyb3JiYXJgIGZ1bmN0aW9uIGFuZCBzcGVjaWZ5IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlIGZvciBvZiB0aGUgZXJyb3IgYmFyLCB0aGUgd2lkdGggY29tbWFuZCBpcyB1c2VkIHRvIHNldCB0aGUgd2lkdGggb2YgdGhlIGVycm9yIGJhciBzbWFsbGVyIHRoYW4gdGhlIHdpZHRoIG9mIHRoZSBiYXIuIAoKYGBge3J9CmFwUmVsU3VtJT4lIAogIGdncGxvdChhZXMoeD10cnQseT1tZWFuKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKyAKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPW1lYW4tc2UseW1heD1tZWFuK3NlKSx3aWR0aD0uMikKYGBgCgotIElzIHRoaXMgcGxvdCBpbmZvcm1hdGl2ZT8/CgotLS0KCiMjIyMgYm94cGxvdHMKCkkgY29uc2lkZXIgYmFycGxvdHMgdG8gYmUgYmFkIHBsb3RzCgotIFRoZXkgYXJlIG5vdCBpbmZvcm1hdGl2ZQotIFRoZXkganVzdCB2aXN1YWxpemUgYSB0d28gcG9pbnQgc3VtbWFyeSBvZiB0aGUgZGF0YS4gSXQgaXMgYmV0dGVyIHRvIGRvIHRoaXMgaW4gYSB0YWJsZQotIFRoZXkgdXNlIGEgbG90IG9mIHNwYWNlIChlLmcuIGZyb20gemVybyB1cCB0byB0aGUgbWluaW11bSByZWxhdGl2ZSBhYnVuZGFuY2UpIHdoZXJlIG5vIGRhdGEgYXJlIHByZXNlbnQuIAoKSXQgaXMgYmV0dGVyIHRvIGdldCBhIHZpZXcgb24gdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YS4gV2UgY2FuIHVzZSBhIGJveHBsb3QgZm9yIHRoaXMgcHVycG9zZS4KV2UgZmlyc3QgZXhwbGFpbiB3aGF0IGEgYm94cGxvdC4gCgotLS0KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyxlY2hvPUZBTFNFfQpmZW0gPC0gTkhBTkVTOjpOSEFORVMgJT4lIGZpbHRlcihHZW5kZXI9PSJmZW1hbGUiICYgIWlzLm5hKERpcmVjdENob2wpKSAlPiUgc2VsZWN0KERpcmVjdENob2wpCmJveHBsb3QoZmVtJERpcmVjdENob2wsIHlsYWI9IkRpcmVjdCBjaG9sZXN0ZXJvbCIsY2V4LmxhYj0xLjUsY2V4LmF4aXM9MS41LGNleC5tYWluPTEuNSkKcmFuZ2VDbDwtcXVhbnRpbGUoZmVtJERpcmVjdENob2wsYyguMjUsLjc1KSkrYygtMSwxKSpkaWZmKHF1YW50aWxlKGZlbSREaXJlY3RDaG9sLGMoLjI1LC43NSkpKSoxLjUKYm94WXM8LWMocmFuZ2UoZmVtJERpcmVjdENob2xbZmVtJERpcmVjdENob2w8PXJhbmdlQ2xbMl0mZmVtJERpcmVjdENob2w+PXJhbmdlQ2xbMV1dKSxxdWFudGlsZShmZW0kRGlyZWN0Q2hvbCxjKC4yNSwuNSwuNzUpKSxyYW5nZUNsWzJdKyhtYXgoZmVtJERpcmVjdENob2wpLXJhbmdlQ2xbMl0pLzIpCnRleHQoMS4zLGJveFlzLGxhYmVscz1jKCJ3aXNrZXIiLCJ3aXNrZXIiLCJ4MjUiLCJtZWRpYWFuIiwieDc1Iiwib3V0bGllcnMiKSxwb3M9NCxjZXg9MS4zKQpsaW5lcyhjKDEuMSwxLjMsMS4zLDEuMSksYyhyYW5nZUNsWzJdLHJhbmdlQ2xbMl0rKG1heChmZW0kRGlyZWN0Q2hvbCktcmFuZ2VDbFsyXSkvMixyYW5nZUNsWzJdKyhtYXgoZmVtJERpcmVjdENob2wpLXJhbmdlQ2xbMl0pLzIsbWF4KGZlbSREaXJlY3RDaG9sKSksbHR5PTIpCmBgYAoKLS0tCgpXZSB3aWxsIG5vdyBtYWtlIGEgYm94cGxvdCBmb3IgdGhlIGFwIGRhdGEKCjEuIFdlIHBpcGUgdGhlIGBhcGAgZGF0YWZyYW1lIHRvIHRoZSBnZ3Bsb3QgY29tbWFuZAoyLiBXZSBzZWxlY3QgdGhlIGRhdGEgd2l0aCB0aGUgY29tbWFuZCBgZ2dwbG90KGFlcyh4PXRydCx5PXJlbCkpYAozLiBXZSBhZGQgYSBib3hwbG90IHdpdGggdGhlIGNvbW1hbmQgYGdlb21fYm94cGxvdCgpYAogICAgCmBgYHtyfQphcCAlPiUgIAogIGdncGxvdChhZXMoeD10cnQseT1yZWwpKSArIAogIGdlb21fYm94cGxvdCgpCmBgYAoKLS0tCgotIE5vdGUsIHRoYXQgd2UgZG8gbm90IGhhdmUgc28gbWFueSBvYnNlcnZhdGlvbnMuCgotIEl0IGlzIGFsd2F5cyBiZXR0ZXIgdG8gc2hvdyB0aGUgZGF0YSBhcyByYXcgYXMgcG9zc2libGUhCgpXZSB3aWxsIG5vdyBhZGQgdGhlIHJhdyBkYXRhIHRvIHRoZSBwbG90LgoKLSBOb3RlIHRoYXQgd2Ugc2V0IHRoZSBvdXRsaWVyLnNoYXBlPU5BIGluIHRoZSBnZW9tX2JveHBsb3QgZnVuY3Rpb24gYmVjYXVzZSBiZWNhdXNlIHdlIHdpbGwgYWRkIGFsbCByYXcgZGF0YSBhbnl3YXkuCi0gV2UgYWRkIHRoZSByYXcgZGF0YSB1c2luZyBgZ2VvbV9wb2ludChwb3NpdGlvbj0iaml0dGVyIilgLCB3aXRoIHRoZSBhcmd1bWVudCBwb3NpdGlvbj0naml0dGVyJyB3ZSB3aWxsIGFkZCBzb21lIHJhbmRvbSBub2lzZSB0byB0aGUgeCBjb29yZGluYXRlIHNvIHRoYXQgd2UgY2FuIHNlZSBhbGwgZGF0YS4KCmBgYHtyfQphcCAlPiUgIAogIGdncGxvdChhZXMoeD10cnQseT1yZWwpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uPSJqaXR0ZXIiKQpgYGAKClRoaXMgaXMgYW4gaW5mb3JtYXRpdmUgcGxvdCEKCi0tLQoKLSBXZSBvYnNlcnZlZCBhbiBlZmZlY3Qgb2YgdGhlIHRyYW5zcGxhbnRhdGlvbiBvbiB0aGUgcmVsYXRpZXZlIGFidW5kYW50aWUgb2YgU3RhcGh5bG9jb2NjdXMuCgotIElzIHRoYXQgZWZmZWN0IGxhcmdlIGVub3VnaCB0byBjb25jbHVkZSB0aGF0IHRoZSB0cmVhdG1lbnQgd29ya3M/IAoKLS0tCgojIyBFc3RpbWF0aW9uIGFuZCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UgCgotIEluZHVjdGlvbjogV2l0aCBzdGF0aXN0aWNhbCBpbmZlcmVuY2Ugd2UgY2FuIGdlbmVyYWxpemUgd2hhdCB3ZSBvYnNlcnZlIGluIHRoZSBzYW1wbGUgdG93YXJkcyB0aGUgcG9wdWxhdGlvbi4gCgotIFRoZSBwcmljZSB0aGF0IHdlIGhhdmUgdG8gcGF5OiB1bmNlcnRhaW50eSBvbiBvdXIgY29uY2x1c2lvbnMhIAoKLS0tCgotIFdpdGggZGF0YSB3ZSBjYW5ub3QgcHJvdmUgdGhhdCB0aGUgdHJlYXRtZW50IHdvcmtzCgotIEZhbHNpZmljYXRpb24gcHJpbmNpcGxlIG9mIFBvcHBlcjogV2l0aCBkYXRhIHdlIGNhbiBvbmx5ICByZWplY3QgYSBoeXBvdGhlc2lzIG9yIHRoZW9yeS4gCgotIFdpdGggc3RhdHMgd2UgY2FuIHRodXMgbm90IHByb3ZlIHRoYXQgdGhlIHRyZWF0bWVudCB3b3Jrcy4gCgotIEJ1dCBzdGF0cyB3aWxsIGFsbG93IHVzIHRvIGZhbGNpZnkgdGhlIG9wcG9zaXRlIGh5cG90aGVzaXM6IGhvdyBtdWNoIGV2aWRlbmNlIGlzIHRoZXJlIGluIHRoZSBkYXRhIGFnYWluc3QgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGVyZSBpcyBubyBlZmZlY3Qgb2YgdGhlIHRyZWF0bWVudD8KCi0gV2l0aCBzdGF0cyB3ZSBjYW4gY2FsY3VsYXRlIGhvdyBsaWtlbHkgaXQgaXMgdG8gZHJhdyBhIHJhbmRvbSBzYW1wbGUgKHdoZW4geW91IHdvdWxkIHJlcGVhdCB0aGUgZXhwZXJpbWVudCkgd2l0aCBhIG1lYW4gZGlmZmVyZW5jZSBpbiByZWxhdGl2ZSBhYnVuZGFuY2UgYmV0d2VlbiB0cmFuc3BsYW50IGFuZCBwbGFjZWJvIGdyb3VwIHRoYXQgaXMgYXQgbGVhc3QgYXMgbGFyZ2UgYXMgd2hhdCB3ZSBvYnNlcnZlZCBpbiBvdXIgc2FtcGxlIHdoZW4gdGhlcmUgd291bGQgYmUgbm8gZWZmZWN0IG9mIHRoZSB0cmVhdG1lbnQuIAoKLSBUaGlzIHByb2JhYmlsaXR5IGlzIGNhbGxlZCBhIHAtdmFsdWUuCgotIElmIHAgaXMgdmVyeSBzbWFsbCwgaXQgaXMgdmVyeSB1bmxpa2VseSB0byBvYnNlcnZlIGEgc2FtcGxlIGxpa2Ugb3VycyBieSByYW5kb20gY2hhbmdlIHdoZW4gdGhlcmUgd291bGQgYmUgbm8gZWZmZWN0IG9mIHRoZSB0cmVhdG1lbnQuIAoKLSBXZSB0eXBpY2FsbHkgY29tcGFyZSBwIHdpdGggNSUuIElmIHRoZXJlIGlzIG5vIGVmZmVjdCBvZiB0aGUgdHJlYXRtZW50IHdlIHdpbGwgdGh1cyB0b2xlcmF0ZSBhIHByb2JhYmlsaXR5IG9mIDUlIG9uIGEgZmFsc2UgcG9zaXRpdmUgY29uY2x1c2lvbi4gCgotIFRvIGNhbGN1bGF0ZSBwIHdlIHdpbGwgaGF2ZSB0byBtb2RlbCB0aGUgZGF0YSB1c2luZyAgc3RhdGlzdGljYWwgbW9kZWxzLgoKLS0tCgpJbiBjaGFwdGVyIDUgd2Ugd2lsbCBsZWFybiB0aGF0IHdlIGNhbiB1c2UgYSB0d28tc2FtcGxlIHQtdGVzdCB0byBnZW5lcmFsaXNlIHdoYXQgd2Ugb2JzZXJ2ZSBpbiB0aGUgbWljcm9iaW9tZSBkYXRhc2V0IHRvd2FyZHMgdGhlIHBvcHVsYXRpb24uIAoKYGBge3J9CnQudGVzdChyZWx+dHJ0LGRhdGE9YXApCmBgYAoKQ29uY2x1c2lvbjoKCldlIGNhbiBjb25jbHVkZSB0aGF0IHRoZSByZWxhdGl2ZSBhYnVuZGFuY2Ugb2YgU3RhcGh5bG9jb2NjdXMgaW4gdGhlIG1pY3JvYmlvbWUgb2YgaW5kaXZpZHVhbHMgd2l0aCBzbWVsbHkgYXJtcGl0cyAgaXMgYHIgZm9ybWF0KGFwUmVsU3VtJG1lYW5bMl0tYXBSZWxTdW0kbWVhblsxXSxkaWdpdHM9MylgJSBoaWdoZXIgdXBvbiB0aGUgdHJhbnNwbGFudCB0aGFuIHVwb24gdGhlIHBsYWNlYm8gdHJlYXRtZW50IChwIDwgMC4wMDEpLiAKCi0tLQoKIyMgU29tZSBjb25jZXB0cwoKV2hhdCBhcmUgdGhlIGNvbnNlcXVlbmNlcyBvZiB1c2luZyBhIHNhbXBsZSBhbmQgcmFuZG9taXNhdGlvbj8KCi0gUmFuZG9tIHNhbXBsaW5nIGlzIGNsb3NlbHkgcmVsYXRlZCB0byB0aGUgY29uY2VwdCBvZiB0aGUgcG9wdWxhdGlvbiBvciB0aGUgc2NvcGUgb2YgdGhlIHN0dWR5LgoKLSBCYXNlZCBvbiBhIHNhbXBsZSBvZiBzdWJqZWN0cywgdGhlIHJlc2VhcmNoZXJzIHdhbnQgdG8gY29tZSB0byBjb25jbHVzaW9ucyB0aGF0IGhvbGQgZm9yCgogICAgLSBhbGwga2luZHMgb2YgcGVvcGxlCiAgICAtIG9ubHkgbWFsZSBzdHVkZW50cwoKLSBTY29wZSBvZiB0aGUgc3R1ZHkgc2hvdWxkIGJlIHdlbGwgc3BlY2lmaWVkIGJlZm9yZSB0aGUgc3RhcnQgb2YgdGhlIHN0dWR5LgoKLSBGb3IgdGhlIHN0YXRpc3RpY2FsIGFuYWx5c2lzIHRvIGJlIHZhbGlkLCBpdCBpcyByZXF1aXJlZCB0aGF0IHRoZSBzdWJqZWN0cyBhcmUgc2VsZWN0ZWQgY29tcGxldGVseSBhdCByYW5kb20gZnJvbSB0aGUgcG9wdWxhdGlvbiB0byB3aGljaCB3ZSB3YW50IHRvIGdlbmVyYWxpemUgb3VyIGNvbmNsdXNpb25zLgoKLSBTZWxlY3RpbmcgY29tcGxldGVseSBhdCByYW5kb20gZnJvbSBhIHBvcHVsYXRpb24gaW1wbGllczoKICAgIC0gYWxsIHN1YmplY3RzIGluIHRoZSBwb3B1bGF0aW9uIHNob3VsZCBoYXZlIHRoZSBzYW1lIHByb2JhYmlsaXR5IG9mIGJlaW5nIHNlbGVjdGVkIGluIHRoZSBzYW1wbGUsCiAgICAtIHRoZSBzZWxlY3Rpb24gb2YgYSBzdWJqZWN0IGluIHRoZSBzYW1wbGUgc2hvdWxkIGJlIGluZGVwZW5kZW50IGZyb20gdGhlIHNlbGVjdGlvbiBvZiB0aGUgb3RoZXIgc3ViamVjdHMgaW4gdGhlIHNhbXBsZS4KCi0gVGhlIHNhbXBsZSBpcyB0aHVzIHN1cHBvc2VkIHRvIGJlIHJlcHJlc2VudGF0aXZlIGZvciB0aGUgcG9wdWxhdGlvbiwgYnV0IHN0aWxsIGl0IGlzIHJhbmRvbS4KCi0gV2hhdCBkb2VzIHRoaXMgaW1wbHk/CgotLS0KCiMgU2FtcGxlIHRvIHNhbXBsZSB2YXJpYWJpbGl0eQoKTmF0aW9uYWwgSGVhbHRoIE5IYW5lcyBzdHVkeQoKICAtIFNpbmNlIDE5NjAgaW5kaXZpZHVhbHMgb2YgYWxsIGFnZXMgYXJlIGludGVydmlld2VkIGluIHRoZWlyIGhvbWVzIGV2ZXJ5IHllYXIKICAtIFRoZSBoZWFsdGggZXhhbWluYXRpb24gY29tcG9uZW50IG9mIHRoZSBzdXJ2ZXkgaXMgY29uZHVjdGVkIGluIGEgbW9iaWxlIGV4YW1pbmF0aW9uIGNlbnRyZSAoTUVDKS4KICAtIFdlIHdpbGwgdXNlIHRoaXMgbGFyZ2Ugc3R1ZHkgdG8gc2VsZWN0IHJhbmRvbSBzdWJqZWN0cyBmcm9tIHRoZSBBbWVyaWNhbiBwb3B1bGF0aW9uLgogIC0gVGhpcyB3aWxsIGhlbHAgdXMgdG8gdW5kZXJzdGFuZCBob3cgdGhlIHJlc3VsdHMgb2YgYW4gYW5hbHlzaXMgYW5kIHRoZSBjb25jbHVzaW9ucyB2YXJ5IGZyb20gc2FtcGxlIHRvIHNhbXBsZS4KCi0tLQoKYGBge3J9CmxpYnJhcnkoTkhBTkVTKQpoZWFkKE5IQU5FUykKZ2xpbXBzZShOSEFORVMpCmBgYAoKLS0tCgojIyBEYXRhIGV4cGxvcmF0aW9uCgoKU3VwcG9zZSB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGFzc2Vzc2luZyB0aGUgZGlmZmVyZW5jZSBpbiBkaXJlY3QgY2hvbGVzdGVyb2wgbGV2ZWxzIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXMgb2xkZXIgdGhhbiAyNSB5ZWFycy4KCjEuIFdlIHBpcGUgdGhlIGRhdGFzZXQgdG8gdGhlIGZ1bmN0aW9uIGBmaWx0ZXJgIHRvIGZpbHRlciB0aGUgZGF0YSBhY2NvcmRpbmcgdG8gYWdlLiAKMi4gV2UgcGxvdCB0aGUgZGlyZWN0IGNob2xlc3Rlcm9sIGxldmVscy4KICAgIC0gV2Ugc2VsZWN0IHRoZSBkYXRhIHdpdGggdGhlIGNvbW1hbmQgYGdncGxvdChhZXMoeD1EaXJlY3RDaG9sKSlgCiAgICAtIFdlIGFkZCBhIGhpc3RvZ3JhbSB3aXRoIHRoZSBjb21tYW5kIGBnZW9tX2hpc3RvZ3JhbSgpYAogICAgLSBXZSBtYWtlIHRvIHZlcnRpY2FsIHBhbmVscyB1c2luZyB0aGUgY29tbWFuZCBgZmFjZXRfZ3JpZChHZW5kZXJ+LilgCiAgICAtIFdlIGN1c3RvbWl6ZSB0aGUgbGFiZWwgb2YgdGhlIHgtYXhpcyB3aXRoIHRoZSBgeGxhYmAgY29tbWFuZC4KCmBgYHtyfQpOSEFORVMlPiVmaWx0ZXIoQWdlPjI1KSU+JQogIGdncGxvdChhZXMoeD1EaXJlY3RDaG9sKSkrCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgZmFjZXRfZ3JpZChHZW5kZXJ+LikgKwogIHhsYWIoIkRpcmVjdCBjaG9sZXN0ZXJvbCAobWcvZGwpIikKYGBgCgotLS0KCi0gQ2hvbGVzdGVyb2wgbGV2ZWxzIGFuZCBjb25jZW50cmF0aW9uIG1lYXN1cmVtZW50cyBhcmUgb2Z0ZW4gc2tld2VkLgotIENvbmNlbnRyYXRpb25zIGNhbm5vdCBiZSBsb3dlciB0aGFuIDAuCi0gVGhleSBhcmUgb2Z0ZW4gbG9nIHRyYW5zZm9ybWVkLgoKYGBge3J9Ck5IQU5FUyU+JQogIGZpbHRlcihBZ2U+MjUpJT4lCiAgZ2dwbG90KGFlcyh4PURpcmVjdENob2wlPiVsb2cyKSkrCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgZmFjZXRfZ3JpZChHZW5kZXJ+LikgKwogIHhsYWIoIkRpcmVjdCBjaG9sZXN0ZXJvbCAobG9nMikiKQpgYGAKCldlIHNlZSB0aGF0IHRoZSBkYXRhIGFyZSBtb3JlIG9yIGxlc3MgYmVsbCBzaGFwZWQgdXBvbiBsb2cgdHJhbnNmb3JtYXRpb24uCgotLS0KCldlIHdpbGwgbm93IGNyZWF0ZSBhIHN1YnNldCBvZiB0aGUgZGF0YSB0aGF0IHdlIHdpbGwgdXNlIHRvIHNhbXBsZSBmcm9tIGluIHRoZSBuZXh0IHNlY3Rpb25zLgoKICAxLiBXZSBmaWx0ZXIgb24gYWdlIGFuZCByZW1vdmUgc3ViamVjdHMgd2l0aCBtaXNzaW5nIHZhbHVlcyAoTkEpLgogIDIuIFdlIG9ubHkgc2VsZWN0IHRoZSB2YXJpYWJsZXMgR2VuZGVyIGFuZCBEaXJlY3RDaG9sIGZyb20gdGhlIGRhdGFzZXQgdG8gYXZvaWQgdW5uZWNlc3NhcnkgdmFyaWFibGVzLgogIDMuIFdpdGggdGhlIG11dGF0ZSBmdW5jdGlvbiB3ZSBjYW4gYWRkIGEgbmV3IHZhcmlhYmxlIGxvZ0Nob2wgd2l0aCBsb2cgdHJhbnNmb3JtZWQgZGlyZWN0IGNob2xlc3Rlcm9sIGxldmVscy4KCmBgYHtyfQpuaGFuZXNTdWI8LSBOSEFORVMlPiUKICBmaWx0ZXIoQWdlPjI1JiFpcy5uYShEaXJlY3RDaG9sKSkgJT4lCiAgc2VsZWN0KGMoIkdlbmRlciIsIkRpcmVjdENob2wiKSkgJT4lCiAgbXV0YXRlKGNob2xMb2c9bG9nMihEaXJlY3RDaG9sKSkKYGBgCgotLS0KCldlIHdpbGwgY2FsY3VsYXRlIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBjaG9sTG9nIHZhcmlhYmxlIGZvciBtYWxlcyBhbmQgZmVtYWxlcyBpbiB0aGUgbGFyZ2UgZGF0YXNldC4gClNvIHdlIGdyb3VwIGJ5IEdlbmRlcgoKYGBge3J9CmNob2xMb2dTdW08LSBuaGFuZXNTdWIgJT4lIAogIGdyb3VwX2J5KEdlbmRlcikgJT4lCiAgIHN1bW1hcml6ZV9hdCgiY2hvbExvZyIsCiAgICAgICAgICAgICAgIGxpc3QobWVhbj1+bWVhbiguLG5hLnJtPVRSVUUpLAogICAgICAgICAgICAgICAgICAgIHNkPX5zZCguLG5hLnJtPVRSVUUpLAogICAgICAgICAgICAgICAgICAgIG49ZnVuY3Rpb24oeCkgeCU+JWlzLm5hJT4lYCFgJT4lc3VtKSkgJT4lCiAgbXV0YXRlKHNlPXNkL3NxcnQobikpCgpjaG9sTG9nU3VtCmBgYAoKLS0tCgojIyBFeHBlcmltZW50CgotIFN1cHBvc2UgdGhhdCB3ZSBoYXZlIG5vIGFjY2VzcyB0byBjaG9sZXN0ZXJvbCBsZXZlbHMgb2YgdGhlIEFtZXJpY2FuIHBvcHVsYXRpb24sCi0gd2Ugd2lsbCBoYXZlIHRvIHNldHVwIGFuIGV4cGVyaW1lbnQuCi0gU3VwcG9zZSB3ZSBoYXZlIGEgYnVkZ2V0IGZvciBhc3Nlc3NpbmcgMTAgZmVtYWxlcyBhbmQgMTAgbWFsZXMsCi0gd2Ugd2lsbCBzdWJzZXQgMTAgZmVtYWxlcyBhbmQgMTAgbWFsZXMgYXQgcmFuZG9tIGZyb20gdGhlIEFtZXJpY2FuIHBvcHVsYXRpb24gYW5kIG1lYXN1cmUgdGhlaXIgZGlyZWN0IGNob2xlc3Rlcm9sIGxldmVscy4KCmBgYHtyfQpmZW08LW5oYW5lc1N1YiU+JWZpbHRlcihHZW5kZXI9PSJmZW1hbGUiKSU+JXNhbXBsZV9uKHNpemU9MTApCm1hbDwtbmhhbmVzU3ViJT4lZmlsdGVyKEdlbmRlcj09Im1hbGUiKSU+JXNhbXBsZV9uKHNpemU9MTApCgpzYW1wPC1yYmluZChmZW0sbWFsKQpzYW1wCmBgYAoKLS0tCgpXZSB3aWxsIG5vdyBwbG90IHRoZSBkYXRhIHdpdGggYSBoaXN0b2dyYW0gYW5kIGJveHBsb3RzCgpgYGB7cn0Kc2FtcCAlPiUKICBnZ3Bsb3QoYWVzKHg9Y2hvbExvZykpKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjEpICsKICBmYWNldF9ncmlkKEdlbmRlcn4uKSArCiAgeGxhYigiRGlyZWN0IGNob2xlc3Rlcm9sIChsb2cyKSIpCgpzYW1wJT4lIAogIGdncGxvdChhZXMoeD1HZW5kZXIseT1jaG9sTG9nKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbj0iaml0dGVyIikKYGBgCgotLS0KCldlIHN1bW1hcml6ZSB0aGUgZGF0YQpgYGB7cn0Kc2FtcCAlPiUgCiAgZ3JvdXBfYnkoR2VuZGVyKSAlPiUKICAgc3VtbWFyaXplX2F0KCJjaG9sTG9nIiwKICAgICAgICAgICAgICAgbGlzdChtZWFuPX5tZWFuKC4sbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICAgc2Q9fnNkKC4sbmEucm09VFJVRSksCiAgICAgICAgICAgICAgICAgICAgbj1mdW5jdGlvbih4KSB4JT4laXMubmElPiVgIWAlPiVzdW0pKSAlPiUKICBtdXRhdGUoc2U9c2Qvc3FydChuKSkKYGBgCgpOb3RlIHRoYXQgdGhlIHNhbXBsZSBtZWFuIGlzIGRpZmZlcmVudCBmcm9tIHRoYXQgb2YgdGhlIGxhcmdlIGV4cGVyaW1lbnQgKCJwb3B1bGF0aW9uIikgd2Ugc2FtcGxlZCBmcm9tLiAKCldlIHRlc3QgZm9yIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gTWFsZXMgYW5kIGZlbWFsZXMKCmBgYHtyfQp0LnRlc3QoY2hvbExvZ35HZW5kZXIsc2FtcCx2YXIuZXF1YWw9VFJVRSkKYGBgCgotLS0KCiMjIFJlcGVhdCB0aGUgZXhwZXJpbWVudAoKSWYgd2UgZG8gdGhlIGV4cGVyaW1lbnQgYWdhaW4gd2Ugc2VsZWN0IG90aGVyIHBlb3BsZSBhbmQgd2Ugb2J0YWluIGRpZmZlcmVudCByZXN1bHRzLgoKCmBgYHtyfQpmZW08LW5oYW5lc1N1YiU+JWZpbHRlcihHZW5kZXI9PSJmZW1hbGUiKSU+JXNhbXBsZV9uKHNpemU9MTApCm1hbDwtbmhhbmVzU3ViJT4lZmlsdGVyKEdlbmRlcj09Im1hbGUiKSU+JXNhbXBsZV9uKHNpemU9MTApCgpzYW1wMjwtcmJpbmQoZmVtLG1hbCkKc2FtcDIlPiUKICBnZ3Bsb3QoYWVzKHg9RGlyZWN0Q2hvbCU+JWxvZykpKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjEpICsKICBmYWNldF9ncmlkKEdlbmRlcn4uKSArCiAgeGxhYigiRGlyZWN0IGNob2xlc3Rlcm9sIChsb2cpIikKCnNhbXAyJT4lIAogIGdncGxvdChhZXMoeD1HZW5kZXIseT1jaG9sTG9nKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbj0iaml0dGVyIikKCnNhbXAyICU+JSAKICBncm91cF9ieShHZW5kZXIpICU+JQogICBzdW1tYXJpemVfYXQoImNob2xMb2ciLAogICAgICAgICAgICAgICBsaXN0KG1lYW49fm1lYW4oLixuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICBzZD1+c2QoLixuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICBuPWZ1bmN0aW9uKHgpIHglPiVpcy5uYSU+JWAhYCU+JXN1bSkpICU+JQogIG11dGF0ZShzZT1zZC9zcXJ0KG4pKQoKdC50ZXN0KGNob2xMb2d+R2VuZGVyLHNhbXAyLHZhci5lcXVhbD1UUlVFKQpgYGAKCi0tLQoKIyMgQW5kIGFnYWluCgpgYGB7cn0Kc2V0LnNlZWQoMTI4NTcpCmZlbTwtbmhhbmVzU3ViJT4lZmlsdGVyKEdlbmRlcj09ImZlbWFsZSIpJT4lc2FtcGxlX24oc2l6ZT0xMCkKbWFsPC1uaGFuZXNTdWIlPiVmaWx0ZXIoR2VuZGVyPT0ibWFsZSIpJT4lc2FtcGxlX24oc2l6ZT0xMCkKCnNhbXAzPC1yYmluZChmZW0sbWFsKQpzYW1wMyU+JQogIGdncGxvdChhZXMoeD1EaXJlY3RDaG9sJT4lbG9nKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMSkgKwogIGZhY2V0X2dyaWQoR2VuZGVyfi4pICsKICB4bGFiKCJEaXJlY3QgY2hvbGVzdGVyb2wgKGxvZykiKQoKc2FtcDMlPiUgCiAgZ2dwbG90KGFlcyh4PUdlbmRlcix5PWNob2xMb2cpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uPSJqaXR0ZXIiKQoKCnNhbXAzICU+JSAKICBncm91cF9ieShHZW5kZXIpICU+JQogICBzdW1tYXJpemVfYXQoImNob2xMb2ciLAogICAgICAgICAgICAgICBsaXN0KG1lYW49fm1lYW4oLixuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICBzZD1+c2QoLixuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICBuPWZ1bmN0aW9uKHgpIHglPiVpcy5uYSU+JWAhYCU+JXN1bSkpICU+JQogIG11dGF0ZShzZT1zZC9zcXJ0KG4pKQoKdC50ZXN0KGNob2xMb2d+R2VuZGVyLHNhbXAzLHZhci5lcXVhbD1UUlVFKQpgYGAKCi0tLQoKIyMgU3VtbWFyeQoKLSBCZWNhdXNlIHdlIHNhbXBsZWQgb3RoZXIgc3ViamVjdHMgaW4gZWFjaCBzYW1wbGUsIHdlIG9idGFpbiBkaWZmZXJlbnQgY2hvbGVzdGVyb2wgbGV2ZWxzLgotIEhvd2V2ZXIsIG5vdCBvbmx5IHRoZSBjaG9sZXN0ZXJvbCBsZXZlbHMgZGlmZmVyIGZyb20gc2FtcGxlIHRvIHNhbXBsZSBidXQgYWxzbyB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzOiBtZWFucywgc3RhbmRhcmQgZGV2aWF0aW9ucyBhbmQgc3RhbmRhcmQgZXJyb3JzLgotIE5vdGUsIHRoYXQgaW4gdGhlIGxhc3Qgc2FtcGxlIHRoZSBsb2cgY2hvbGVzdGVyb2wgbGV2ZWxzIGFyZSBvbiBhdmVyYWdlIGxvd2VyIGZvciBmZW1hbGVzIHRoYW4gZm9yIG1hbGVzOyBiYXNlZCBvbiB0aGlzIHNhbXBsZSB3ZSBldmVuIHdvdWxkIHdyb25nbHkgY29uY2x1ZGUgdGhhdCB0aGUgY2hvbGVzdGVyb2wgbGV2ZWxzIGZvciBmZW1hbGVzIGFyZSBvbiBhdmVyYWdlIGxhcmdlciB0aGFuIHRob3NlIG9mIG1hbGVzLgoKLSBUaGlzIGltcGxpZXMgdGhhdCBvdXIgY29uY2x1c2lvbnMgYXJlIGFsc28gc3ViamVjdGVkIHRvIHVuY2VydGFpbnR5IGFuZCBtaWdodCBjaGFuZ2UgZnJvbSBzYW1wbGUgdG8gc2FtcGxlLgoKLSBTYW1wbGVzIGFzIHRoZSBvbmUgd2hlcmUgdGhlIGVmZmVjdCBzd2FwcyBhbmQgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCwgaG93ZXZlciwgYXJlIHZlcnkgcmFyZS4KLSBUaGlzIGlzIGlsbHVzdHJhdGVkIHdpdGggdGhlIGNvZGUgYmVsb3csIHdoZXJlIHdlIHdpbGwgZHJhdyAyMDAwMCByZXBlYXRlZCBzYW1wbGVzIHdpdGggc2FtcGxlIHNpemUgMTAgZm9yIGZlbWFsZXMgYW5kIG1hbGVzIGZyb20gdGhlIE5IYW5lcyBzdHVkeS4KCmBgYHtyfQpuc2ltPC0yMDAwMApuU2FtcDwtMTAKcmVzPC1tYXRyaXgoMCxucm93PW5zaW0sbmNvbD0yKQpmZW08LW5oYW5lc1N1YiU+JWZpbHRlcihHZW5kZXI9PSJmZW1hbGUiKQptYWw8LW5oYW5lc1N1YiU+JWZpbHRlcihHZW5kZXI9PSJtYWxlIikKCmZvciAoaSBpbiAxOm5zaW0pCnsKIGZlbVNhbXA8LXNhbXBsZShmZW0kY2hvbExvZyxuU2FtcCkKIG1hbFNhbXA8LXNhbXBsZShtYWwkY2hvbExvZyxuU2FtcCkKCm1lYW5GZW08LW1lYW4oZmVtU2FtcCkKIG1lYW5NYWw8LW1lYW4obWFsU2FtcCkKIGRlbHRhPC1tZWFuRmVtLW1lYW5NYWwKIHNkRmVtPC1zZChmZW1TYW1wKQogc2RNYWw8LXNkKG1hbFNhbXApCiBzZUZlbTwtc2RGZW0vc3FydChuU2FtcCkKIHNlRmVtPC1zZEZlbS9zcXJ0KG5TYW1wKQogc2RQb29sPC1zcXJ0KChzZEZlbV4yKihuU2FtcC0xKSArIHNkTWFsXjIqKG5TYW1wLTEpKS8oMipuU2FtcC0yKSkKdHZhbHVlPC0oZGVsdGEpLyhzZFBvb2wqc3FydCgxL25TYW1wKzEvblNhbXApKQpwdmFsdWU8LXB0KGFicyh0dmFsdWUpLGxvd2VyLnRhaWwgPSBGQUxTRSxkZj0yKm5TYW1wLTIpKjIKIHJlc1tpLF08LWMoZGVsdGEscHZhbHVlKQp9CnN1bShyZXNbLDJdPDAuMDUmcmVzWywxXT4wKQpzdW0ocmVzWywyXT4wLjA1KQpzdW0ocmVzWywyXTwwLjA1JnJlc1ssMV08MCkKCnJlczwtcmVzICU+JSBhcy5kYXRhLmZyYW1lCm5hbWVzKHJlcykgPC0gYygiZGVsdGEiLCJwdmFsdWUiKQpyZXMgJT4lIAogIGdncGxvdChhZXMoeD1kZWx0YSx5PS1sb2cxMChwdmFsdWUpLGNvbG9yPXB2YWx1ZTwwLjA1KSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICB4bGFiKCJBdmVyYWdlIGNob2xlc3Rlcm9sIGRpZmZlcmVuY2UiKSArCiAgeWxhYigiLSBsb2cxMChwdmFsdWUpIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJyZWQiKSkKCnJlcyAlPiUgCiAgZ2dwbG90KGFlcyh5PWRlbHRhKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4PTAseT1jKG1lYW4oZmVtJGNob2xMb2cpLW1lYW4obWFsJGNob2xMb2cpKSxjb2xvcj0icG9wLiBkaWZmIikpICsKICB4bGFiKCIiKQpgYGAKCk9ubHkgaW4gYHIgc3VtKHJlc1ssMl08MC4wNSZyZXNbLDFdPDApYCBvdXQgb2YgMjAwMDAgc2FtcGxlcyB3ZSBjb25jbHVkZSB0aGF0IHRoZSBtZWFuIGNob2xlc3Rlcm9sIGxldmVsIG9mIG1hbGVzIGlzIHNpZ25pZmljYW50bHkgbG93ZXIgdGhhbiBmb3IgZmVtYWxlcy4gRm9yIHRoZSByZW1haW5pbmcgc2FtcGxlcyB0aGUgY2hvbGVzdGVyb2wgbGV2ZWxzIGZvciBtYWxlcyB3ZXJlIG9uIGF2ZXJhZ2Ugc2lnbmlmaWNhbnRseSBsb3dlciB0aGFuIGZvciBmZW1hbGVzIChgciBzdW0ocmVzWywyXTwwLjA1JnJlc1ssMV0+MClgIHNhbXBsZXMpIG9yIHRoZSBhdmVyYWdlIGRpZmZlcmVuY2UgaW4gY2hvbGVzdGVyb2wgbGV2ZWxzIHdlcmUgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKGByIHN1bShyZXNbLDJdPjAuMDUpYCBzYW1wbGVzKS4gVGhlIGxhdHRlciBpcyBiZWNhdXNlIHRoZSBwb3dlciBpcyByYXRoZXIgbG93IHRvIGRldGVjdCB0aGUgZGlmZmVyZW5jZSB3aXRoIDEwIHNhbXBsZXMgaW4gZWFjaCBncm91cC4KCi0tLQoKIyMgQXNzaWdubWVudAoKMS4gQ29weSB0aGUgY29kZSBjaHVuayB3aXRoIHRoZSBzaW11bGF0aW9uIHN0dWR5CjIuIEFkZCBpdCBoZXJlIGJlbG93CjMuIE1vZGlmeSB0aGUgc2FtcGxlIHNpemUgdG8gNTAuCjQuIFdoYXQgZG8geW91IG9ic2VydmU/CgoKLS0tCgojIyBDb250cm9sIG9mIGZhbHNlIHBvc2l0aXZlcwoKV2F0IGhhcHBlbnMgd2hlbiB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gYm90aCBncm91cHM/IAoKLSBXZSB3aWxsIGhhdmUgdG8gc2ltdWxhdGUgZXhwZXJpbWVudHMgZm9yIHdoaWNoIHRoZSBjaG9sZXN0b3JvbCBsZXZlbHMgYXJlIHRoZSBzYW1lIGZvciBib3RoIGdyb3Vwcy4gCgoKLSBXZSBjYW4gZG8gdGhpcyBieSBzYW1wbGluZyBkYXRhIGZvciBib3RoIGdyb3VwcyBmcm9tIHRoZSBzdWJzZXQgb2Ygd29tZW4gaW4gdGhlIHN0dWR5LiAKCi0gV2UgZG8gdGhpcyBhZ2FpbiBmb3IgMTAgc3ViamVjdHMgcGVyIGdyb3VwCgoKYGBge3J9Cm5zaW08LTIwMDAwCm5TYW1wPC0xMApyZXM8LW1hdHJpeCgwLG5yb3c9bnNpbSxuY29sPTIpCmZlbTwtbmhhbmVzU3ViJT4lZmlsdGVyKEdlbmRlcj09ImZlbWFsZSIpCm1hbDwtbmhhbmVzU3ViJT4lZmlsdGVyKEdlbmRlcj09Im1hbGUiKQoKZm9yIChpIGluIDE6bnNpbSkKewogZmVtU2FtcDwtc2FtcGxlKGZlbSRjaG9sTG9nLG5TYW1wKQogZmVtMlNhbXA8LXNhbXBsZShmZW0kY2hvbExvZyxuU2FtcCkKCm1lYW5GZW08LW1lYW4oZmVtU2FtcCkKIG1lYW5GZW0yPC1tZWFuKGZlbTJTYW1wKQogZGVsdGE8LW1lYW5GZW0tbWVhbkZlbTIKIHNkRmVtPC1zZChmZW1TYW1wKQogc2RGZW0yPC1zZChmZW0yU2FtcCkKIHNlRmVtPC1zZEZlbS9zcXJ0KG5TYW1wKQogc2VGZW08LXNkRmVtMi9zcXJ0KG5TYW1wKQogc2RQb29sPC1zcXJ0KChzZEZlbV4yKihuU2FtcC0xKSArIHNkRmVtMl4yKihuU2FtcC0xKSkvKDIqblNhbXAtMikpCnR2YWx1ZTwtKGRlbHRhKS8oc2RQb29sKnNxcnQoMS9uU2FtcCsxL25TYW1wKSkKcHZhbHVlPC1wdChhYnModHZhbHVlKSxsb3dlci50YWlsID0gRkFMU0UsZGY9MipuU2FtcC0yKSoyCiByZXNbaSxdPC1jKGRlbHRhLHB2YWx1ZSkKfQpzdW0ocmVzWywyXTwwLjA1JnJlc1ssMV0+MCkKc3VtKHJlc1ssMl0+PTAuMDUpCnN1bShyZXNbLDJdPDAuMDUmcmVzWywxXTwwKQoKcmVzPC1yZXMgJT4lIGFzLmRhdGEuZnJhbWUKbmFtZXMocmVzKSA8LSBjKCJkZWx0YSIsInB2YWx1ZSIpCnJlcyAlPiUgCiAgZ2dwbG90KGFlcyh4PWRlbHRhLHk9LWxvZzEwKHB2YWx1ZSksY29sb3I9cHZhbHVlPDAuMDUpKSArIAogIGdlb21fcG9pbnQoKSArIAogIHhsYWIoIkF2ZXJhZ2UgY2hvbGVzdGVyb2wgZGlmZmVyZW5jZSIpICsKICB5bGFiKCItIGxvZzEwKHB2YWx1ZSkiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIpKQoKcmVzICU+JSAKICBnZ3Bsb3QoYWVzKHk9ZGVsdGEpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHg9MCx5PTAsY29sb3I9InBvcC4gZGlmZiIpKSArCiAgeGxhYigiIikKYGBgCgpOb3RlLCB0aGF0IHRoZSBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGFyZSBvbiBgciBzdW0ocmVzWywyXTwwLjA1KWAgb24gYHIgZm9ybWF0KG5zaW0sZGlnaXRzPTUpYCBleHBlcmltZW50cyBhbmQgYXJlIG5pY2VseSBjb250cm9sbGVkIGF0IHRoZSA1JSBsZXZlbC4KCldoYXQgaGFwcGVucyBpZiB3ZSBpbmNyZWFzZSB0aGUgc2FtcGxlIHNpemUgdG8gNTAgc3ViamVjdHMgcGVyIGdyb3VwPyAgCgoKLS0tCgojIFNhbGsgU3R1ZHkKCi0gSW4gMTkxNiwgdGhlIFVTIGV4cGVyaWVuY2VkIHRoZSBmaXJzdCBsYXJnZSBlcGlkZW1pYyBvZiBwb2xpby4KLSBKb2huIFNhbGsgZGV2ZWxvcGVkIGEgdmFjY2luZSB3aXRoIHByb21pc2luZyByZXN1bHRzIGluIHRoZSBsYWIgaW4gdGhlIGVhcmx5IGZpZnRpZXMuCi0gSW4gMTk1NCwgdGhlIE5hdGlvbmFsIEZvdW5kYXRpb24KZm9yIEluZmFudGlsZSBQYXJhbHlzaXMgKE5GSVApIGhhcyBzZXR1cCBhIGxhcmdlIHN0dWR5IHRvIGFzc2VzcyB0aGUgZWZmZWN0aXZlbmVzcyBvZiB0aGUgU2FsayB2YWNjaW5lLgotIFN1cHBvc2UgdGhhdCB0aGUgTkZJUCB3b3VsZCBoYXZlIHZhY2NpbmF0ZWQgYSBsYXJnZSBudW1iZXIgb2YgY2hpbGRyZW4gaW4gMTk1NCBhbmQgd291bGQgaGF2ZSBvYnNlcnZlZCB0aGF0IHRoZSBwb2xpbyBpbmNpZGVuY2UgaW4gMTk1NCB3YXMgbG93ZXIgdGhhbiBpbiAxOTUzLiBDb3VsZCB0aGV5IGhhdmUgY29uY2x1ZGVkIHRoYXQgdGhlIHZhY2NpbmUgd2FzIGVmZmVjdGl2ZT8KCi0tLQoKIyMgTkZJUCBTdHVkeQojIyMgRGVzaWduCgotIExhcmdlIHNpbXVsdGFuZW91cyBzdHVkeSB3aXRoIGNhc2VzLCB2YWNjaW5hdGVkIGNoaWxkcmVuLCBhbmQgY29udHJvbHMsICBub24tdmFjY2luYXRlZCBjaGlsZHJlbi4KLSBBbGwgc2Nob29scyBpbiBkaXN0cmljdHMgd2l0aCBoaWdoIHBvbGlvIGluY2lkZW5jZQotIENhc2VzOiBjaGlsZHJlbiB3aXRoIGNvbnNlbnQgZm9yIHZhY2NpbmF0aW9uIGZyb20gc2Vjb25kIGdyYWRlIG9mIHByaW1hcnkgc2Nob29sLgotIENvbnRyb2xzOiBjaGlsZHJlbiBmcm9tICBmaXJzdCBhbmQgdGhpcmQgZ3JhZGUuCgojIyMgRGF0YQpgYGB7cn0KbmZpcDwtZGF0YS5mcmFtZShncm91cD1jKCJjYXNlcyIsImNvbnRyb2wiLCJub0NvbmNlbnQiKSxncmFkZT1jKCJnMiIsImcxZzMiLCJnMiIpLHZhY2Npbj1jKCJ5ZXMiLCJubyIsIm5vIiksdG90YWw9YygyMjE5OTgsNzI1MTczLDEyMzYwNSkscG9saW89Yyg1NCwzOTEsNTYpKQpuZmlwJG5vUG9saW88LW5maXAkdG90YWwtbmZpcCRwb2xpbwprbml0cjo6a2FibGUobmZpcCkKYGBgCgpDb21wYXJlIHBvbGlvIGluY2lkZW5jZT8KCmBgYHtyLCBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQpuZmlwJGluY2lkZW5jZVBNPC1yb3VuZChuZmlwJHBvbGlvL25maXAkdG90YWwqMWU2LDApCmtuaXRyOjprYWJsZShuZmlwKQpgYGAKCldoYXQgY2FuIHdlIGNvbmNsdWRlPwoKLS0tCgojIyBDb25mb3VuZGluZwoKCmBgYHtyLGVjaG89RkFMU0UsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLG91dC53aWR0aCA9ICc1MCUnfQpwbG90KGMoMCwwLDEpLGMoLTIsMiwwKSxwY2g9YygiUyIsIlYiLCJQIikseGF4dD0ibm9uZSIseWF4dD0ibm9uZSIsYXhlcz1GQUxTRSx4bGFiPSIiLHlsYWI9IiIsY2V4PTQseWxpbT1jKC0yLjIsMi4yKSkKYXJyb3dzKHgwPTAuMSx4MT0uOSx5MD0xLjgseTE9MC4xLGx3ZD00KQphcnJvd3MoeDA9MC4xLHgxPS45LHkwPS0xLjgseTE9LTAuMixsd2Q9NCkKYXJyb3dzKHgwPTAseDE9MCx5MD0tMS40LHkxPTEuNCxsd2Q9NCkKYGBgCgoKLSBXZSBvYnNlcnZlIGEgbG93ZXIgcG9saW8gKFApIGluY2lkZW5jZSBmb3IgY2hpbGRyZW4gZm9yIHdobyBubyBjb25zZW50IHdhcyBnaXZlbiB0aGFuIGZvciB0aGUgY2hpbGRyZW4gaW4gdGhlIGNvbnRyb2wgZ3JvdXAuCgotIENvbnNlbnQgZm9yIHZhY2NpbmF0aW9uIChWKSB3YXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBzb2Npby1lY29ub21pYyBzdGF0dXMgKFMpLgoKLSBDaGlsZHJlbiBvZiBsb3dlciBzb2Npby1lY29ub21pYyBzdGF0dXMgd2VyZSBtb3JlIHJlc2lzdGFudCB0byB0aGUgZGlzZWFzZS4KCi0gVGhlIGdyb3VwcyBvZiBjYXNlcyBhbmQgY29udHJvbHMgYXJlIG5vdCBjb21wYXJhYmxlOgogICAgLSBkaWZmZXJlbmNlIGluIGFnZSwKICAgIC0gZGlmZmVyZW5jZSBpbiBzb2Npby1lY29ub21pYyBzdGF0dXMgYW5kCiAgICAtIGRpZmZlcmVuY2UgaW4gc3VzY2VwdGlibGUgZm9yIGRpc2Vhc2UuCgotLS0KCiMjIFNhbGsgU3R1ZHkKCiMjIyBEZXNpZ24KQSBuZXcgc3R1ZHkgd2FzIGNvbmR1Y3RlZDogUmFuZG9taXplZCBkb3VibGUgYmxpbmQgc3R1ZHkKCiAgLSBDaGlsZHJlbiBhcmUgYXNzaWduZWQgYXQgcmFuZG9tIHRvIHRoZSBjb250cm9sIG9yIGNhc2UgdHJlYXRtZW50IGFybSBhZnRlciBjb25zZW50IHdhcyBnaXZlbiBieSB0aGUgcGFyZW50cy4KICAtIENvbnRyb2w6IHZhY2NpbmF0aW9uIHdpdGggcGxhY2VibwogIC0gVHJlYXRtZW50OiB2YWNjaW5hdGlvbiB3aXRoIHZhY2NpbmUKICAtIGRvdWJsZSBibGluZGluZzoKICAgIC0gcGFyZW50cyBkaWQgbm90IGtub3cgaWYgdGhlaXIgY2hpbGQgd2FzIHZhY2NpbmF0ZWQgb3IgcmVjZWl2ZWQgdGhlIHBsYWNlYm8KICAgIC0gY2FyZS1naXZlci9yZXNlYXJjaGVycyBkaWQgbm90IGtub3cgaWYgdGhlIGNoaWxkIHdhcyB2YWNjaW5hdGVkICBvciByZWNlaXZlZCBwbGFjZWJvCgotLS0KCiMjIyBEYXRhCgpgYGB7cn0Kc2FsazwtZGF0YS5mcmFtZShncm91cD1jKCJjYXNlcyIsImNvbnRyb2wiLCJub0NvbmNlbnQiKSx0cmVhdG1lbnQ9YygidmFjY2luZSIsInBsYWNlYm8iLCJub25lIiksdG90YWw9YygyMDA3NDUsCjIwMTIyOSwgMzM4Nzc4KSxwb2xpbz1jKDU3LDE0MiwxNTcpKQpzYWxrJG5vUG9saW88LXNhbGskdG90YWwtc2FsayRwb2xpbwpzYWxrJGluY2lkZW5jZVBNPC1yb3VuZChzYWxrJHBvbGlvL3NhbGskdG90YWwqMWU2LDApCmtuaXRyOjprYWJsZShzYWxrKQpgYGAKCi0gV2Ugb2JzZXJ2ZSBhIG11Y2ggbGFyZ2VyIGVmZmVjdCBub3cgdGhhdCB0aGUgY2FzZXMgYW5kIHRoZSBjb250cm9scyBhcmUgY29tcGFyYWJsZSwgaW5jaWRlbmNlIG9mIGByIHNhbGskaW5jaWRlbmNlUE1bMV1gICBhbmQgYHIgc2FsayRpbmNpZGVuY2VQTVsyXWAgcGVyIG1pbGxpb24sIHJlc3BlY3RpdmVseS4KCi0gVGhlIHBvbGlvIGluY2lkZW5jZSBmb3IgY2hpbGRyZW4gd2l0aCBubyBjb25zZW50IHJlbWFpbnMgc2ltaWxhciwgYHIgbmZpcCRpbmNpZGVuY2VQTVszXWAgYW5kIGByIHNhbGskaW5jaWRlbmNlUE1bM11gIHBlciBtaWxsaW9uIGluIHRoZSBORklQIGFuZCBTYWxrIHN0dWR5LCByZXNwZWN0aXZlbHkuCgotLS0KCiMgUm9sZSBvZiBTdGF0aXN0aWNzIGluIHRoZSBMaWZlIFNjaWVuY2VzCgotIFdlIGhhdmUgc2VlbiB0aGF0CiAgICAtIGl0IGlzIGltcG9ydGFudCB0byBjYXJlZnVsbHkgc3BlY2lmeSB0aGUgc2NvcGUgb2YgdGhlIHN0dWR5IGJlZm9yZSB0aGUgZXhwZXJpbWVudCwKICAgIC0gdGhlIHNhbXBsZSBzaXplIG1hdHRlcnMsCiAgICAtIHdlIHNob3VsZCBiZSBhd2FyZSBvZiBjb25mb3VuZGluZywgYW5kIAogICAgLSBhIHByb3BlciBjb250cm9sIGlzIHJlcXVpcmVkLgoKJFxyaWdodGFycm93JCBHb29kIGV4cGVyaW1lbnRhbCBkZXNpZ24gaXMgY3J1Y2lhbCEKCi0gV2UgYWxzbyBvYnNlcnZlZCB0aGF0IHRoZXJlIGlzIHZhcmlhYmlsaXR5IGluIHRoZSBwb3B1bGF0aW9uIGFuZCBiZWNhdXNlIHdlIGNhbiBvbmx5IHNhbXBsZSBhIHNtYWxsIHBhcnQgb2YgdGhlIHBvcHVsYXRpb24gb3VyIHJlc3VsdHMgYW5kIGNvbmNsdXNpb25zIGFyZSBzdWJqZWN0ZWQgdG8gdW5jZXJ0YWludHkuCgoKCgotIFN0YXRpc3RpY3MgaXMgdGhlIHNjaWVuY2Ugb24KICAgIDEuIGNvbGxlY3RpbmcgKGV4cGVyaW1lbnRhbCBkZXNpZ24pLAogICAgMi4gZXhwbG9yaW5nIChkYXRhIGV4cGxvcmF0aW9uKSBhbmQKICAgIDMuIGxlYXJuaW5nIGZyb20gZGF0YSBhbmQgdG8gZ2VuZXJhbGl6ZSB3aGF0IHdlIG9ic2VydmUgaW4gdGhlIHNhbXBsZSB0b3dhcmRzIHRoZSBwb3B1bGF0aW9uIHdoaWxlIHF1YW50aWZ5aW5nLCBjb250cm9sbGluZyBhbmQgcmVwb3J0aW5nIHZhcmlhYmlsaXR5IGFuZCB1bmNlcnRhaW50eSAoc3RhdGlzdGljYWwgbW9kZWxsaW5nIGFuZCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UpLgoKLSBUaGVyZWZvcmUsIHN0YXRpc3RpY3MgcGxheXMgYW4gaW1wb3J0YW50IHJvbGUgaW4gYWxtb3N0IGFsbCBzY2llbmNlcyAoZS5nLiBjb2x1bW4gInBvaW50cyBvZiBzaWduaWZpY2FuY2UiIGluIE5hdHVyZSBNZXRob2RzLiBodHRwOi8vYmxvZ3MubmF0dXJlLmNvbS9tZXRoYWdvcmEvMjAxMy8wOC9naXZpbmdfc3RhdGlzdGljc190aGVfYXR0ZW50aW9uX2l0X2Rlc2VydmVzLmh0bWwpCgotLS0KCmBgYHtyIHBvcDJTYW1wMlBvcCwgb3V0LndpZHRoPSc4MCUnLGZpZy5hc3A9LjgsIGZpZy5hbGlnbj0nY2VudGVyJyxlY2hvPUZBTFNFfQppZiAoInBpIiVpbiVscygpKSBybSgicGkiKQprb3B2b2V0ZXI8LWZ1bmN0aW9uKHgseSxhbmdsZT0wLGw9LjIsY2V4LmRvdD0uNSxwY2g9MTksY29sPSJibGFjayIpCnsKYW5nbGU9YW5nbGUvMTgwKnBpCnBvaW50cyh4LHksY2V4PWNleC5kb3QscGNoPXBjaCxjb2w9Y29sKQpsaW5lcyhjKHgseCtsKmNvcygtcGkvMithbmdsZSkpLGMoeSx5K2wqc2luKC1waS8yK2FuZ2xlKSksY29sPWNvbCkKbGluZXMoYyh4K2wvMipjb3MoLXBpLzIrYW5nbGUpLHgrbC8yKmNvcygtcGkvMithbmdsZSkrbC80KmNvcyhhbmdsZSkpLGMoeStsLzIqc2luKC1waS8yK2FuZ2xlKSx5K2wvMipzaW4oLXBpLzIrYW5nbGUpK2wvNCpzaW4oYW5nbGUpKSxjb2w9Y29sKQpsaW5lcyhjKHgrbC8yKmNvcygtcGkvMithbmdsZSkseCtsLzIqY29zKC1waS8yK2FuZ2xlKStsLzQqY29zKHBpK2FuZ2xlKSksYyh5K2wvMipzaW4oLXBpLzIrYW5nbGUpLHkrbC8yKnNpbigtcGkvMithbmdsZSkrbC80KnNpbihwaSthbmdsZSkpLGNvbD1jb2wpCmxpbmVzKGMoeCtsKmNvcygtcGkvMithbmdsZSkseCtsKmNvcygtcGkvMithbmdsZSkrbC8yKmNvcygtcGkvMitwaS80K2FuZ2xlKSksYyh5K2wqc2luKC1waS8yK2FuZ2xlKSx5K2wqc2luKC1waS8yK2FuZ2xlKStsLzIqc2luKC1waS8yK3BpLzQrYW5nbGUpKSxjb2w9Y29sKQpsaW5lcyhjKHgrbCpjb3MoLXBpLzIrYW5nbGUpLHgrbCpjb3MoLXBpLzIrYW5nbGUpK2wvMipjb3MoLXBpLzItcGkvNCthbmdsZSkpLGMoeStsKnNpbigtcGkvMithbmdsZSkseStsKnNpbigtcGkvMithbmdsZSkrbC8yKnNpbigtcGkvMi1waS80K2FuZ2xlKSksY29sPWNvbCkKfQoKcGFyKG1hcj1jKDAsMCwwLDApLG1haT1jKDAsMCwwLDApKQpwbG90KDAsMCx4bGFiPSIiLHlsYWI9IiIseGxpbT1jKDAsMTApLHlsaW09YygwLDEwKSxjb2w9MCx4YXh0PSJub25lIix5YXh0PSJub25lIixheGVzPUZBTFNFKQpyZWN0KDAsNiwxMCwxMCxib3JkZXI9InJlZCIsbHdkPTIpCnRleHQoLjUsOCwicG9wdWxhdGlvbiIsc3J0PTkwLGNvbD0icmVkIixjZXg9MikKc3ltYm9scyAoMywgOCwgY2lyY2xlcz0xLjUsIGNvbD0icmVkIixhZGQ9VFJVRSxmZz0icmVkIixpbmNoZXM9RkFMU0UsbHdkPTIpCnNldC5zZWVkKDMzMCkKZ3JpZD1zZXEoMCwxLjMsLjAxKQoKZm9yIChpIGluIDE6NTApCnsKCWFuZ2xlMT1ydW5pZihuPTEsbWluPTAsbWF4PTM2MCkKCWFuZ2xlMj1ydW5pZihuPTEsbWluPTAsbWF4PTM2MCkKCXJhZGl1cz1zYW1wbGUoZ3JpZCxwcm9iPWdyaWReMipwaS9zdW0oZ3JpZF4yKnBpKSxzaXplPTEpCglrb3B2b2V0ZXIoMytyYWRpdXMqY29zKGFuZ2xlMS8xODAqcGkpLDgrcmFkaXVzKnNpbihhbmdsZTEvMTgwKnBpKSxhbmdsZT1hbmdsZTIpCn0KdGV4dCg3LjUsOCwiY2hvbGVzdGVyb2wgaW4gcG9wdWxhdGlvbiIsY29sPSJyZWQiLGNleD0xLjIpCgpyZWN0KDAsMCwxMCw0LGJvcmRlcj0iYmx1ZSIsbHdkPTIpCnRleHQoLjUsMiwic2FtcGxlIixzcnQ9OTAsY29sPSJibHVlIixjZXg9MikKc3ltYm9scyAoMywgMiwgY2lyY2xlcz0xLjUsIGNvbD0icmVkIixhZGQ9VFJVRSxmZz0iYmx1ZSIsaW5jaGVzPUZBTFNFLGx3ZD0yKQpmb3IgKGkgaW4gMDoyKQoJZm9yIChqIGluIDA6NCkKewoKCWtvcHZvZXRlcigyLjEraiooMy45LTIuMSkvNCwxLjEraSkKfQp0ZXh0KDcuNSwyLCJjaG9sZXN0ZXJvbCBpbiBzYW1wbGUiLGNvbD0iYmx1ZSIsY2V4PTEuMikKCmFycm93cygzLDUuOSwzLDQuMSxjb2w9ImJsYWNrIixsd2Q9MykKYXJyb3dzKDcsNC4xLDcsNS45LGNvbD0iYmxhY2siLGx3ZD0zKQp0ZXh0KDEuNSw1LCJFWFAuIERFU0lHTiAoMSkiLGNvbD0iYmxhY2siLGNleD0xLjIpCnRleHQoOC41LDUsIkVTVElNQVRJT04gJlxuSU5GRVJFTkNFICgzKSIsY29sPSJibGFjayIsY2V4PTEuMikKdGV4dCg3LjUsLjUsIkRBVEEgRVhQTE9SQVRJT04gJlxuREVTQ1JJUFRJVkUgU1RBVElTVElDUyAoMikiLGNvbD0iYmxhY2siLGNleD0xLjIpCmBgYAoKCg==