Chapter 13 Elementary Statistics

Statistics is the practice of turning data into information to identify trends and understand features of populations.

13.1 Describing Raw Data

Raw data= observations.
Varialbe is a characteristics of an individual in a population.

13.1.1 Numeric Variables

Numeric variables are observations recorded as numbers.
The two types are: discrete and continuous
Discrete numeric variables are observations that you can COUNT.
Continuous numeric variables can take an infinite value within a range.

13.1.2 Categorical Variables

Categorical variables are also known as discrete or qualitative variables. Categorical variables can be further categorized as either nominal, ordinal or dichotomous.

Nominal variables are variables that have two or more categories, but which can not be logicaly ranked. For example, a real estate agent could classify their types of property into distinct categories such as houses, condos, co-ops or bungalows. So “type of property” is a nominal variable with 4 categories called houses, condos, co-ops and bungalows. Of note, the different categories of a nominal variable can also be referred to as groups or levels of the nominal variable. Another example of a nominal variable would be classifying where people live in the USA by state. In this case there will be many more levels of the nominal variable (50 in fact).

Dichotomous variables are nominal variables which have only two categories or levels. For example, if we were looking at gender, we would most probably categorize somebody as either “male” or “female”. This is an example of a dichotomous variable (and also a nominal variable). Another example might be if we asked a person if they owned a mobile phone. Here, we may categorise mobile phone ownership as either “Yes” or “No”. In the real estate agent example, if type of property had been classified as either residential or commercial then “type of property” would be a dichotomous variable.

Ordinal variables are variables that can be ordered or ranked. They have two or more categories just like nominal variables. So if you asked someone if they liked the policies of the Democratic Party and they could answer either “Not very much”, “They are OK” or “Yes, a lot” then you have an ordinal variable. Why? Because you have 3 categories, namely “Not very much”, “They are OK” and “Yes, a lot” and you can rank them from the most positive (Yes, a lot), to the middle response (They are OK), to the least positive (Not very much). However, whilst we can rank the levels, we cannot place a “value” to them; we cannot say that “They are OK” is twice as positive as “Not very much” for example.

# examine the dataframe chickwts
chickwts[1:5,]
# describe chickwts
(?chickwts)

Here is the output :
Description

An experiment was conducted to measure and compare the effectiveness of various feed supplements on the growth rate of chickens.

Usage

chickwts
Format

A data frame with 71 observations on the following 2 variables.

weight a numeric variable giving the chick weight.

feed
a factor giving the feed type.

Details

Newly hatched chicks were randomly allocated into six groups, and each group was given a different feed supplement. Their weights in grams after six weeks are given along with feed types.

# so we take a look at weight and feed
chickwts$weight
 [1] 179 160 136 227 217 168 108 124 143 140 309 229 181 141 260 203 148 169 213 257 244 271 243 230 248
[26] 327 329 250 193 271 316 267 199 171 158 248 423 340 392 339 341 226 320 295 334 322 297 318 325 257
[51] 303 315 380 153 263 242 206 344 258 368 390 379 260 404 318 352 359 216 222 283 332
chickwts$feed
 [1] horsebean horsebean horsebean horsebean horsebean horsebean horsebean horsebean horsebean horsebean
[11] linseed   linseed   linseed   linseed   linseed   linseed   linseed   linseed   linseed   linseed  
[21] linseed   linseed   soybean   soybean   soybean   soybean   soybean   soybean   soybean   soybean  
[31] soybean   soybean   soybean   soybean   soybean   soybean   sunflower sunflower sunflower sunflower
[41] sunflower sunflower sunflower sunflower sunflower sunflower sunflower sunflower meatmeal  meatmeal 
[51] meatmeal  meatmeal  meatmeal  meatmeal  meatmeal  meatmeal  meatmeal  meatmeal  meatmeal  casein   
[61] casein    casein    casein    casein    casein    casein    casein    casein    casein    casein   
[71] casein   
Levels: casein horsebean linseed meatmeal soybean sunflower

From the values, weight is a numeric variables (continuous)
From the values, feed is a categorical variable (nominal)

13.1.3 Univariate and Multivariate Data

Univariate data are related to only ONE variable
MUltivariate data can’t be used stand alone. (ie. gps lat and long)

quakes[1:5,]
plot(quakes$long,quakes$lat,xlab="longitude",ylab="Latitude",main="Wilson plot")

13.1.4 Parameter or Statistics?

A statistic and a parameter are very similar. They are both descriptions of groups, like ???50% of dog owners prefer X Brand dog food.??? The difference between a statistic and a parameter is that statistics describe a sample. A parameter describes an entire population.

13.2 Summary Statistics

NOw lets calculate some statistics with R.

13.2.1 Centrality: Mean, Median, Mode

Central tendency refers to the idea that there is one number that best summarizes the entire set of measurements, a number that is in some way “central” to the set.

The mode.
The mode is the measurement that has the greatest frequency, (the most common!). Although it isn’t used that much, it is useful when differences are rare or when the differences are non numerical. The prototypical example of something is usually the mode.

The mode for our example is 3.2. It is the grade with the most people (3).

The median.
The median is the number at which half your measurements are more than that number and half are less than that number. The median is actually a better measure of centrality than the mean if your data are skewed, meaning lopsided.

If, for example, you have a dozen ordinary folks and one millionaire, the distribution of their wealth would be lopsided towards the ordinary people, and the millionaire would be an outlier, or highly deviant member of the group. The millionaire would influence the mean a great deal, making it seem like all the members of the group are doing quite well. The median would actually be closer to the mean of all the people other than the millionaire.

The median for our example is 3.0. Half the people scored lower, and half higher (and one exactly).

The mean.
The mean is just the average. It is the sum of all your measurements, divided by the number of measurements. This is the most used measure of central tendency, because of its mathematical qualities. It works best if the data is distributed very evenly across the range, or is distributed in the form of a normal or bell-shaped curve (see below). One interesting thing about the mean is that it represents the expected value if the distribution of measurements were random! Here is what the formula looks like:

= _{i=1}^N x_i =

So 3.0 + 2.8 + 2.8 + 2.4 + 3.2 + 2.8 + 1.8 + 3.8 + 2.6 + 3.4 + 2.4 + 4.0 + 3.4 + 3.2 + 3.2 is 43.8. Divide that by 15 and that is the mean or average for our example: 2.92.

xdata<-c(2,4.4,3,3,2,2.2,2,4)
xdata
[1] 2.0 4.4 3.0 3.0 2.0 2.2 2.0 4.0

Let’s compute the stats:

x.bar<-mean(xdata)
x.bar
[1] 2.825

Compute for median

m.bar<-median(xdata)
m.bar
[1] 2.6

Finding mode in R using table functions, which gives you the frequencies you need:

xtab<-table(xdata)
xtab
xdata
  2 2.2   3   4 4.4 
  3   1   2   1   1 
# now find the min and the max
min(xdata)
[1] 2
max(xdata)
[1] 4.4
# to get both in one go
range(xdata)
[1] 2.0 4.4
# when max is applied to a table it will output the frequencies
max(xtab)
[1] 3

Since the number 2 was seen 3 times. finally you can construct a logical flag vector to get the mode from the table.

d.bar <-xtab[xtab==max(xtab)]
d.bar
2 
3 

The first number is the number with the highest mode, and the second number is the frequency. Let’s look at the quake dataset

Qtab<-table(quakes$mag)
Qtab[Qtab==max(Qtab)]
4.5 
107 

This gives us the magnitude (4.5) that occured the most (107 times)

# when computing mean with Na or Nan values use na.rm=TRUE
test<-c(1,4,6,NA)
test1<-c(1,4,6,NaN)
mean(test)
[1] NA
mean(test1)
[1] NaN
# use na.rm=true
mean(test,na.rm=TRUE)
[1] 3.666667
mean(test1,na.rm=TRUE)
[1] 3.666667

The benefit of [tapply] Suppose you wanted to compute summary stats

Instead of doing this:

mean(chickwts$weight[chickwts$feed=="casein"])
[1] 323.5833
mean(chickwts$weight[chickwts$feed=="horsebean"])
[1] 160.2
mean(chickwts$weight[chickwts$feed=="linseed"])
[1] 218.75
mean(chickwts$weight[chickwts$feed=="meatmeal"])
[1] 276.9091
mean(chickwts$weight[chickwts$feed=="soybean"])
[1] 246.4286
mean(chickwts$weight[chickwts$feed=="sunflower"])
[1] 328.9167
  

YOu could use the [tapply]

tapply(chickwts$weight, INDEX=chickwts$feed,FUN=mean)
   casein horsebean   linseed  meatmeal   soybean sunflower 
 323.5833  160.2000  218.7500  276.9091  246.4286  328.9167 

Easier!

13.2.2 Counts Percentages, and Proportions

It is useful to know the number of observations of categorical variables.

# find the number of each type of feeds
table(chickwts$feed)

   casein horsebean   linseed  meatmeal   soybean sunflower 
       12        10        12        11        14        12 
# then to get the proportions
table(chickwts$feed)/nrow(chickwts)

   casein horsebean   linseed  meatmeal   soybean sunflower 
0.1690141 0.1408451 0.1690141 0.1549296 0.1971831 0.1690141 

YOu can also use the fact that TRUE counts as 1 and false counts as zero.

sum(chickwts$feed=="soybean")/nrow(chickwts)
mean(chickwts$feed=="soybean")

Then we can get into more complicated cases, like what is the mean of soybean or horsebean?

mean(chickwts$feed=="soybean"|chickwts$feed=="horsebean")

You can again use the [tapply]

tapply(chickwts$weight,INDEX=chickwts$feed,FUN=function(x) length(x)/nrow(chickwts))
   casein horsebean   linseed  meatmeal   soybean sunflower 
0.1690141 0.1408451 0.1690141 0.1549296 0.1971831 0.1690141 

You can use the [round] function to round out the numbers


round(table(chickwts$feed)/nrow(chickwts),digits = 3)

13.2.3 Quartiles, Percentiles, and the 5 number summary

A quantile is a value computed from a collection of numeric measurements that indicates an observation’s rank when compared to all the other present observations.

Quantiles divide the observations in a sample in the same way.
So the median is a quantile as it divides the samples into 2.
It can also be called percentile as in the pTH quantile is equivalent to 100 x pTH percentile.

# review the contents of xdata
xdata
[1] 2.0 4.4 3.0 3.0 2.0 2.2 2.0 4.0
# get the quantile
quantile(xdata,prob=0.8)
80% 
3.6 
quantile(xdata,prob=c(0,0.25,.5,.75,1))
  0%  25%  50%  75% 100% 
2.00 2.00 2.60 3.25 4.40 

The above is called the ‘five number summary’ of xdata.

summary(xdata)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.000   2.600   2.825   3.250   4.400 
# compute the lower and upper quartiles of the weights of the chicks in the chickwts.
quantile(chickwts$weight, prob=c(0.25,.75))
  25%   75% 
204.5 323.5 
# compute using quake data
summary(quakes$mag[quakes$depth<400])
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   4.00    4.40    4.60    4.67    4.90    6.40 

13.2.4 Spread: Variance, Standard Deviation, and Interquartile Range

Spread measures how dispersed your data are.

xdata
[1] 2.0 4.4 3.0 3.0 2.0 2.2 2.0 4.0
ydata=c(1,4.4,1,3,2,2.2,2,7)
ydata
[1] 1.0 4.4 1.0 3.0 2.0 2.2 2.0 7.0
mean(xdata)
[1] 2.825
mean(ydata)
[1] 2.825

NOtice that both xdata and ydata have the same mean. Let us visualize them by plotting

plot(xdata,type="n", xlab="", ylab="data vector", yaxt="n",bty="n")
abline(h=c(3,3.5),lty=2, col="gray")
abline(v=2.825,lwd=2,lty=3)
text(c(0.8,0.8),c(3,3.5),labels=c("x","y"))
points(jitter(c(xdata,ydata)),c(rep(3,length(xdata)),rep(3.5,length(ydata))))

comparing two hypothetical data vectors that share an identical mean.
Variance measures the degree of the spread of numeric observations aroudn their arithmetic mean.
Standard deviation is the square root of the variance. Interquartile range (IQR) measures the width of the middle 50 percent of the data that lie within a 25 percent quartile on either side of the median. As such it is the difference between the upper and lower quartiles of your data.

var(xdata)
[1] 0.9078571
sd(xdata)
[1] 0.9528154
IQR(xdata)
[1] 1.25
# confirm relationship between variance and standard deviation
sqrt(var(xdata))
[1] 0.9528154
as.numeric(quantile(xdata,0.75)-quantile(xdata,0.25))
[1] 1.25

0.9528154 is also the value for standard deviation.
1.25 is also the value of the IQR

Now compute for ydata

sd(ydata)
[1] 2.012639
IQR(ydata)
[1] 1.6
var(ydata)
[1] 4.050714

These confirm that the ydata results are more spreadout from the mean (the vertical dotted line in the plot above)

# what is the standard deviation of chickwts?
sd(chickwts$weight)
[1] 78.0737

This means that each chick is on the average 78.0737 grams away from the mean weight.

13.2.5 Covariance and Correlation

Use correlation to analyse the relationship between two numeric variables to assess trends.
Covariance expresses how much tow numeric variables “change together”
Positve results mean that they are positively correlated (the figures move in the same direction)
Negative results mean that they are negatively correlated (the figures move in opposite direction)

The relationship grows weaker the nearer it is to zero. Correlation allows you to interpret the covariance furhter by identifying both the direction and the strength of any association.

# recall xdata and ydata
xdata
[1] 2.0 4.4 3.0 3.0 2.0 2.2 2.0 4.0
ydata
[1] 1.0 4.4 1.0 3.0 2.0 2.2 2.0 7.0
cov(xdata,ydata)
[1] 1.479286
cov(xdata,ydata)/(sd(xdata)*sd(ydata))
[1] 0.7713962
cor(xdata,ydata)
[1] 0.7713962

Plotting…

plot(xdata,ydata,pch=13,cex=1.5)

Looking at the quake dataset and plotting them

plot(quakes$mag,quakes$stations,xlab="magnitude",ylab="No of stations")

cov(quakes$mag,quakes$stations)
[1] 7.508181
cor(quakes$mag,quakes$stations)
[1] 0.8511824

Remember correlation allows you to measure association. Not causation.

13.2.6 Outliers

Outliers are observations that does not appear to fit with the rest of hte data. It usually presents itself as extreme values.

# example of outliers by plotting
foo<-c(0.6,-0.6,.1,-0.2,-1.0,0.4,.3,-1.8,1.1,6.0)
foo
 [1]  0.6 -0.6  0.1 -0.2 -1.0  0.4  0.3 -1.8  1.1  6.0
plot(foo,rep(0,10,yaxt="n",ylab="",bty="n",cex=2,cex.axis=1.5,cex.lab=1.5))
abline(h=0,col="gray",lty=2)
arrows(5,0.5,5.9,0.1,lwd=2)
text(5,0.7,labels="outlier?",cex=3)

# bivarate example
bar<-c(0.1,.3,1.3,.6,.2,-1.7,.8,.9,-.8,-1)
baz<-c(-0.3,.9,2.8,2.3,1.2,-4.1,-.4,4.1,-2.3,-100)
plot(bar,baz,axes=T,cex=2,cex.axis=1.5,cex.lab=1.5)
arrows(-.5,-80,-.94,-97,lwd=2)
text(-0.45,-74,labels="outlier?",cex=3)

LS0tDQp0aXRsZTogIkNoYXB0ZXIgMTMgQmFzaWMgU3RhdGlzdGljcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxoMT4gQ2hhcHRlciAxMyBFbGVtZW50YXJ5IFN0YXRpc3RpY3MgPC9oMT4NClN0YXRpc3RpY3MgaXMgdGhlIHByYWN0aWNlIG9mIHR1cm5pbmcgZGF0YSBpbnRvIGluZm9ybWF0aW9uIHRvIGlkZW50aWZ5IHRyZW5kcyBhbmQgdW5kZXJzdGFuZCBmZWF0dXJlcyBvZiBwb3B1bGF0aW9ucy4NCg0KPGgyPiAxMy4xIERlc2NyaWJpbmcgUmF3IERhdGEgPC9oMj4NClJhdyBkYXRhPSBvYnNlcnZhdGlvbnMuPC9icj4NClZhcmlhbGJlIGlzIGEgY2hhcmFjdGVyaXN0aWNzIG9mIGFuIGluZGl2aWR1YWwgaW4gYSBwb3B1bGF0aW9uLjwvYnI+DQoNCjxoMz4gMTMuMS4xIE51bWVyaWMgVmFyaWFibGVzIDwvaDM+DQpOdW1lcmljIHZhcmlhYmxlcyBhcmUgb2JzZXJ2YXRpb25zIHJlY29yZGVkIGFzIG51bWJlcnMuPC9icj4NClRoZSB0d28gdHlwZXMgYXJlOiBkaXNjcmV0ZSBhbmQgY29udGludW91czwvYnI+DQo8Yj5EaXNjcmV0ZTwvYj4gbnVtZXJpYyB2YXJpYWJsZXMgYXJlIG9ic2VydmF0aW9ucyB0aGF0IHlvdSBjYW4gQ09VTlQuPC9icj4NCjxiPkNvbnRpbnVvdXM8L2I+IG51bWVyaWMgdmFyaWFibGVzIGNhbiB0YWtlIGFuIGluZmluaXRlIHZhbHVlIHdpdGhpbiBhIHJhbmdlLjwvYnI+DQoNCjxoMz4gMTMuMS4yIENhdGVnb3JpY2FsIFZhcmlhYmxlcyA8L2gzPg0KQ2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSBhbHNvIGtub3duIGFzIDxiPmRpc2NyZXRlIG9yIHF1YWxpdGF0aXZlIHZhcmlhYmxlczwvYj4uIENhdGVnb3JpY2FsIHZhcmlhYmxlcyBjYW4gYmUgZnVydGhlciBjYXRlZ29yaXplZCBhcyBlaXRoZXIgbm9taW5hbCwgb3JkaW5hbCBvciBkaWNob3RvbW91cy48L1A+DQoNCk5vbWluYWwgdmFyaWFibGVzIGFyZSB2YXJpYWJsZXMgdGhhdCBoYXZlIHR3byBvciBtb3JlIGNhdGVnb3JpZXMsIGJ1dCB3aGljaCBjYW4gbm90IGJlIGxvZ2ljYWx5IHJhbmtlZC4gRm9yIGV4YW1wbGUsIGEgcmVhbCBlc3RhdGUgYWdlbnQgY291bGQgY2xhc3NpZnkgdGhlaXIgdHlwZXMgb2YgcHJvcGVydHkgaW50byBkaXN0aW5jdCBjYXRlZ29yaWVzIHN1Y2ggYXMgaG91c2VzLCBjb25kb3MsIGNvLW9wcyBvciBidW5nYWxvd3MuIFNvICJ0eXBlIG9mIHByb3BlcnR5IiBpcyBhIG5vbWluYWwgdmFyaWFibGUgd2l0aCA0IGNhdGVnb3JpZXMgY2FsbGVkIGhvdXNlcywgY29uZG9zLCBjby1vcHMgYW5kIGJ1bmdhbG93cy4gT2Ygbm90ZSwgdGhlIGRpZmZlcmVudCBjYXRlZ29yaWVzIG9mIGEgbm9taW5hbCB2YXJpYWJsZSBjYW4gYWxzbyBiZSByZWZlcnJlZCB0byBhcyBncm91cHMgb3IgbGV2ZWxzIG9mIHRoZSBub21pbmFsIHZhcmlhYmxlLiBBbm90aGVyIGV4YW1wbGUgb2YgYSBub21pbmFsIHZhcmlhYmxlIHdvdWxkIGJlIGNsYXNzaWZ5aW5nIHdoZXJlIHBlb3BsZSBsaXZlIGluIHRoZSBVU0EgYnkgc3RhdGUuIEluIHRoaXMgY2FzZSB0aGVyZSB3aWxsIGJlIG1hbnkgbW9yZSBsZXZlbHMgb2YgdGhlIG5vbWluYWwgdmFyaWFibGUgKDUwIGluIGZhY3QpLjwvUD4NCg0KRGljaG90b21vdXMgdmFyaWFibGVzIGFyZSBub21pbmFsIHZhcmlhYmxlcyB3aGljaCBoYXZlIG9ubHkgdHdvIGNhdGVnb3JpZXMgb3IgbGV2ZWxzLiBGb3IgZXhhbXBsZSwgaWYgd2Ugd2VyZSBsb29raW5nIGF0IGdlbmRlciwgd2Ugd291bGQgbW9zdCBwcm9iYWJseSBjYXRlZ29yaXplIHNvbWVib2R5IGFzIGVpdGhlciAibWFsZSIgb3IgImZlbWFsZSIuIFRoaXMgaXMgYW4gZXhhbXBsZSBvZiBhIGRpY2hvdG9tb3VzIHZhcmlhYmxlIChhbmQgYWxzbyBhIG5vbWluYWwgdmFyaWFibGUpLiBBbm90aGVyIGV4YW1wbGUgbWlnaHQgYmUgaWYgd2UgYXNrZWQgYSBwZXJzb24gaWYgdGhleSBvd25lZCBhIG1vYmlsZSBwaG9uZS4gSGVyZSwgd2UgbWF5IGNhdGVnb3Jpc2UgbW9iaWxlIHBob25lIG93bmVyc2hpcCBhcyBlaXRoZXIgIlllcyIgb3IgIk5vIi4gSW4gdGhlIHJlYWwgZXN0YXRlIGFnZW50IGV4YW1wbGUsIGlmIHR5cGUgb2YgcHJvcGVydHkgaGFkIGJlZW4gY2xhc3NpZmllZCBhcyBlaXRoZXIgcmVzaWRlbnRpYWwgb3IgY29tbWVyY2lhbCB0aGVuICJ0eXBlIG9mIHByb3BlcnR5IiB3b3VsZCBiZSBhIGRpY2hvdG9tb3VzIHZhcmlhYmxlLjwvUD4NCg0KT3JkaW5hbCB2YXJpYWJsZXMgYXJlIHZhcmlhYmxlcyB0aGF0IGNhbiAgYmUgb3JkZXJlZCBvciByYW5rZWQuIFRoZXkgaGF2ZSB0d28gb3IgbW9yZSBjYXRlZ29yaWVzIGp1c3QgbGlrZSBub21pbmFsIHZhcmlhYmxlcy4gU28gaWYgeW91IGFza2VkIHNvbWVvbmUgaWYgdGhleSBsaWtlZCB0aGUgcG9saWNpZXMgb2YgdGhlIERlbW9jcmF0aWMgUGFydHkgYW5kIHRoZXkgY291bGQgYW5zd2VyIGVpdGhlciAiTm90IHZlcnkgbXVjaCIsICJUaGV5IGFyZSBPSyIgb3IgIlllcywgYSBsb3QiIHRoZW4geW91IGhhdmUgYW4gb3JkaW5hbCB2YXJpYWJsZS4gV2h5PyBCZWNhdXNlIHlvdSBoYXZlIDMgY2F0ZWdvcmllcywgbmFtZWx5ICJOb3QgdmVyeSBtdWNoIiwgIlRoZXkgYXJlIE9LIiBhbmQgIlllcywgYSBsb3QiIGFuZCB5b3UgY2FuIHJhbmsgdGhlbSBmcm9tIHRoZSBtb3N0IHBvc2l0aXZlIChZZXMsIGEgbG90KSwgdG8gdGhlIG1pZGRsZSByZXNwb25zZSAoVGhleSBhcmUgT0spLCB0byB0aGUgbGVhc3QgcG9zaXRpdmUgKE5vdCB2ZXJ5IG11Y2gpLiBIb3dldmVyLCB3aGlsc3Qgd2UgY2FuIHJhbmsgdGhlIGxldmVscywgd2UgY2Fubm90IHBsYWNlIGEgInZhbHVlIiB0byB0aGVtOyB3ZSBjYW5ub3Qgc2F5IHRoYXQgIlRoZXkgYXJlIE9LIiBpcyB0d2ljZSBhcyBwb3NpdGl2ZSBhcyAiTm90IHZlcnkgbXVjaCIgZm9yIGV4YW1wbGUuPC9QPg0KDQoNCmBgYHtyfQ0KIyBleGFtaW5lIHRoZSBkYXRhZnJhbWUgY2hpY2t3dHMNCmNoaWNrd3RzWzE6NSxdDQoNCmBgYA0KDQpgYGB7cn0NCiMgZGVzY3JpYmUgY2hpY2t3dHMNCig/Y2hpY2t3dHMpDQpgYGANCkhlcmUgaXMgdGhlIG91dHB1dCA6PC9icj4NCkRlc2NyaXB0aW9uPC9icj4NCg0KQW4gZXhwZXJpbWVudCB3YXMgY29uZHVjdGVkIHRvIG1lYXN1cmUgYW5kIGNvbXBhcmUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgdmFyaW91cyBmZWVkIHN1cHBsZW1lbnRzIG9uIHRoZSBncm93dGggcmF0ZSBvZiBjaGlja2Vucy48L2JyPg0KDQpVc2FnZTwvYnI+DQoNCmNoaWNrd3RzPC9icj4NCkZvcm1hdDwvYnI+DQoNCkEgZGF0YSBmcmFtZSB3aXRoIDcxIG9ic2VydmF0aW9ucyBvbiB0aGUgZm9sbG93aW5nIDxiPjIgdmFyaWFibGVzLjwvYj4NCg0KPGI+d2VpZ2h0PC9iPg0KYSBudW1lcmljIHZhcmlhYmxlIGdpdmluZyB0aGUgY2hpY2sgd2VpZ2h0LjwvYnI+DQoNCjxiPmZlZWQ8L2I+PC9icj4NCmEgZmFjdG9yIGdpdmluZyB0aGUgZmVlZCB0eXBlLjwvYnI+DQoNCkRldGFpbHM8L2JyPg0KDQpOZXdseSBoYXRjaGVkIGNoaWNrcyB3ZXJlIHJhbmRvbWx5IGFsbG9jYXRlZCBpbnRvIHNpeCBncm91cHMsIGFuZCBlYWNoIGdyb3VwIHdhcyBnaXZlbiBhIGRpZmZlcmVudCBmZWVkIHN1cHBsZW1lbnQuIFRoZWlyIHdlaWdodHMgaW4gZ3JhbXMgYWZ0ZXIgc2l4IHdlZWtzIGFyZSBnaXZlbiBhbG9uZyB3aXRoIGZlZWQgdHlwZXMuPC9icj4NCg0KDQpgYGB7cn0NCiMgc28gd2UgdGFrZSBhIGxvb2sgYXQgd2VpZ2h0IGFuZCBmZWVkDQpjaGlja3d0cyR3ZWlnaHQNCmNoaWNrd3RzJGZlZWQNCmBgYA0KDQpGcm9tIHRoZSB2YWx1ZXMsIHdlaWdodCBpcyBhIG51bWVyaWMgdmFyaWFibGVzIChjb250aW51b3VzKSA8L2JyPg0KRnJvbSB0aGUgdmFsdWVzLCBmZWVkIGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgKG5vbWluYWwpPC9icj4NCg0KPGgzPiAxMy4xLjMgVW5pdmFyaWF0ZSBhbmQgTXVsdGl2YXJpYXRlIERhdGEgPC9oMz4NClVuaXZhcmlhdGUgZGF0YSBhcmUgcmVsYXRlZCB0byBvbmx5IE9ORSB2YXJpYWJsZTwvYnI+DQpNVWx0aXZhcmlhdGUgZGF0YSBjYW4ndCBiZSB1c2VkIHN0YW5kIGFsb25lLiAoaWUuIGdwcyBsYXQgYW5kIGxvbmcpPC9icj4NCg0KYGBge3J9DQpxdWFrZXNbMTo1LF0NCg0KYGBgDQoNCmBgYHtyfQ0KcGxvdChxdWFrZXMkbG9uZyxxdWFrZXMkbGF0LHhsYWI9ImxvbmdpdHVkZSIseWxhYj0iTGF0aXR1ZGUiLG1haW49IldpbHNvbiBwbG90IikNCmBgYA0KDQoNCg0KPGgzPiAxMy4xLjQgUGFyYW1ldGVyIG9yIFN0YXRpc3RpY3M/IDwvaDM+DQoNCkEgc3RhdGlzdGljIGFuZCBhIHBhcmFtZXRlciBhcmUgdmVyeSBzaW1pbGFyLiBUaGV5IGFyZSBib3RoIGRlc2NyaXB0aW9ucyBvZiBncm91cHMsIGxpa2UgPz8/NTAlIG9mIGRvZyBvd25lcnMgcHJlZmVyIFggQnJhbmQgZG9nIGZvb2QuPz8/IFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYSBzdGF0aXN0aWMgYW5kIGEgcGFyYW1ldGVyIGlzIHRoYXQgc3RhdGlzdGljcyBkZXNjcmliZSBhIHNhbXBsZS4gQSBwYXJhbWV0ZXIgZGVzY3JpYmVzIGFuIGVudGlyZSBwb3B1bGF0aW9uLjwvYnI+DQoNCjxoMj4gMTMuMiBTdW1tYXJ5IFN0YXRpc3RpY3MgPC9oMj4NCk5PdyBsZXRzIGNhbGN1bGF0ZSBzb21lIHN0YXRpc3RpY3Mgd2l0aCBSLiA8L3A+DQoNCjxoMz4gMTMuMi4xIENlbnRyYWxpdHk6IE1lYW4sIE1lZGlhbiwgTW9kZSA8L2gzPg0KQ2VudHJhbCB0ZW5kZW5jeSByZWZlcnMgdG8gdGhlIGlkZWEgdGhhdCB0aGVyZSBpcyBvbmUgbnVtYmVyIHRoYXQgYmVzdCBzdW1tYXJpemVzIHRoZSBlbnRpcmUgc2V0IG9mIG1lYXN1cmVtZW50cywgYSBudW1iZXIgdGhhdCBpcyBpbiBzb21lIHdheSA8Yj4iY2VudHJhbCI8L2I+IHRvIHRoZSBzZXQuDQoNClRoZSBtb2RlLiAgPGJyPg0KVGhlIG1vZGUgaXMgdGhlIG1lYXN1cmVtZW50IHRoYXQgaGFzIHRoZSBncmVhdGVzdCBmcmVxdWVuY3ksICh0aGUgbW9zdCBjb21tb24hKS4gIEFsdGhvdWdoIGl0IGlzbid0IHVzZWQgdGhhdCBtdWNoLCBpdCBpcyB1c2VmdWwgd2hlbiBkaWZmZXJlbmNlcyBhcmUgcmFyZSBvciB3aGVuIHRoZSBkaWZmZXJlbmNlcyBhcmUgbm9uIG51bWVyaWNhbC4gIFRoZSBwcm90b3R5cGljYWwgZXhhbXBsZSBvZiBzb21ldGhpbmcgaXMgdXN1YWxseSB0aGUgbW9kZS48L3A+DQoNClRoZSBtb2RlIGZvciBvdXIgZXhhbXBsZSBpcyAzLjIuICBJdCBpcyB0aGUgZ3JhZGUgd2l0aCB0aGUgbW9zdCBwZW9wbGUgKDMpLjwvcD4NCg0KVGhlIG1lZGlhbi4gPC9icj4NClRoZSBtZWRpYW4gaXMgdGhlIG51bWJlciA8Yj5hdCB3aGljaCBoYWxmIHlvdXIgbWVhc3VyZW1lbnRzIGFyZSBtb3JlIHRoYW4gdGhhdCBudW1iZXIgYW5kIGhhbGYgYXJlIGxlc3MgdGhhbiB0aGF0IG51bWJlcjwvYj4uICBUaGUgbWVkaWFuIGlzIGFjdHVhbGx5IGEgYmV0dGVyIG1lYXN1cmUgb2YgY2VudHJhbGl0eSB0aGFuIHRoZSBtZWFuIGlmIHlvdXIgZGF0YSBhcmUgc2tld2VkLCBtZWFuaW5nIGxvcHNpZGVkLiAgPC9wPg0KDQpJZiwgZm9yIGV4YW1wbGUsIHlvdSBoYXZlIGEgZG96ZW4gb3JkaW5hcnkgZm9sa3MgYW5kIG9uZSBtaWxsaW9uYWlyZSwgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVpciB3ZWFsdGggd291bGQgYmUgbG9wc2lkZWQgdG93YXJkcyB0aGUgb3JkaW5hcnkgcGVvcGxlLCBhbmQgdGhlIG1pbGxpb25haXJlIHdvdWxkIGJlIGFuIG91dGxpZXIsIG9yIGhpZ2hseSBkZXZpYW50IG1lbWJlciBvZiB0aGUgZ3JvdXAuICBUaGUgbWlsbGlvbmFpcmUgd291bGQgaW5mbHVlbmNlIHRoZSBtZWFuIGEgZ3JlYXQgZGVhbCwgbWFraW5nIGl0IHNlZW0gbGlrZSBhbGwgdGhlIG1lbWJlcnMgb2YgdGhlIGdyb3VwIGFyZSBkb2luZyBxdWl0ZSB3ZWxsLiAgVGhlIG1lZGlhbiB3b3VsZCBhY3R1YWxseSBiZSBjbG9zZXIgdG8gdGhlIG1lYW4gb2YgYWxsIHRoZSBwZW9wbGUgb3RoZXIgdGhhbiB0aGUgbWlsbGlvbmFpcmUuPC9wPg0KDQpUaGUgbWVkaWFuIGZvciBvdXIgZXhhbXBsZSBpcyAzLjAuICBIYWxmIHRoZSBwZW9wbGUgc2NvcmVkIGxvd2VyLCBhbmQgaGFsZiBoaWdoZXIgKGFuZCBvbmUgZXhhY3RseSkuPC9wPg0KDQpUaGUgbWVhbi48L2JyPg0KVGhlIG1lYW4gaXMganVzdCB0aGUgYXZlcmFnZS4gSXQgaXMgdGhlIHN1bSBvZiBhbGwgeW91ciBtZWFzdXJlbWVudHMsIGRpdmlkZWQgYnkgdGhlIG51bWJlciBvZiBtZWFzdXJlbWVudHMuICBUaGlzIGlzIHRoZSBtb3N0IHVzZWQgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRlbmN5LCBiZWNhdXNlIG9mIGl0cyBtYXRoZW1hdGljYWwgcXVhbGl0aWVzLiAgSXQgd29ya3MgYmVzdCBpZiB0aGUgZGF0YSBpcyBkaXN0cmlidXRlZCB2ZXJ5IGV2ZW5seSBhY3Jvc3MgdGhlIHJhbmdlLCBvciBpcyBkaXN0cmlidXRlZCBpbiB0aGUgZm9ybSBvZiBhIG5vcm1hbCBvciBiZWxsLXNoYXBlZCBjdXJ2ZSAoc2VlIGJlbG93KS4gIE9uZSBpbnRlcmVzdGluZyB0aGluZyBhYm91dCB0aGUgbWVhbiBpcyB0aGF0IGl0IHJlcHJlc2VudHMgdGhlIGV4cGVjdGVkIHZhbHVlIGlmIHRoZSBkaXN0cmlidXRpb24gb2YgbWVhc3VyZW1lbnRzIHdlcmUgcmFuZG9tISAgSGVyZSBpcyB3aGF0IHRoZSBmb3JtdWxhIGxvb2tzIGxpa2U6PC9icj4NCg0KXG92ZXJsaW5le3h9ID0gXGZyYWN7MX17Tn1cc3VtX3tpPTF9Xk4geF9pID0gXGZyYWN7eF8xK3hfMitcY2RvdHMreF9OfXtOfSA8L3A+DQoNClNvIDMuMCArIDIuOCArIDIuOCArIDIuNCArIDMuMiArIDIuOCArIDEuOCArIDMuOCArIDIuNiArIDMuNCArIDIuNCArIDQuMCArIDMuNCArIDMuMiArIDMuMiBpcyA0My44LiAgRGl2aWRlIHRoYXQgYnkgMTUgYW5kIHRoYXQgaXMgdGhlIG1lYW4gb3IgYXZlcmFnZSBmb3Igb3VyIGV4YW1wbGU6IDIuOTIuDQoNCmBgYHtyfQ0KeGRhdGE8LWMoMiw0LjQsMywzLDIsMi4yLDIsNCkNCnhkYXRhDQpgYGANCkxldCdzIGNvbXB1dGUgdGhlIHN0YXRzOg0KDQpgYGB7cn0NCnguYmFyPC1tZWFuKHhkYXRhKQ0KeC5iYXINCmBgYA0KQ29tcHV0ZSBmb3IgbWVkaWFuDQpgYGB7cn0NCm0uYmFyPC1tZWRpYW4oeGRhdGEpDQptLmJhcg0KDQpgYGANCg0KRmluZGluZyBtb2RlIGluIFIgdXNpbmcgdGFibGUgZnVuY3Rpb25zLCB3aGljaCBnaXZlcyB5b3UgdGhlIGZyZXF1ZW5jaWVzIHlvdSBuZWVkOg0KDQpgYGB7cn0NCnh0YWI8LXRhYmxlKHhkYXRhKQ0KeHRhYg0KDQojIG5vdyBmaW5kIHRoZSBtaW4gYW5kIHRoZSBtYXgNCm1pbih4ZGF0YSkNCm1heCh4ZGF0YSkNCiMgdG8gZ2V0IGJvdGggaW4gb25lIGdvDQpyYW5nZSh4ZGF0YSkNCmBgYA0KDQpgYGB7cn0NCiMgd2hlbiBtYXggaXMgYXBwbGllZCB0byBhIHRhYmxlIGl0IHdpbGwgb3V0cHV0IHRoZSBmcmVxdWVuY2llcw0KbWF4KHh0YWIpDQpgYGANClNpbmNlIHRoZSBudW1iZXIgMiB3YXMgc2VlbiAzIHRpbWVzLiANCmZpbmFsbHkgeW91IGNhbiBjb25zdHJ1Y3QgYSBsb2dpY2FsIGZsYWcgdmVjdG9yIHRvIGdldCB0aGUgbW9kZSBmcm9tIHRoZSB0YWJsZS4NCg0KYGBge3J9DQpkLmJhciA8LXh0YWJbeHRhYj09bWF4KHh0YWIpXQ0KZC5iYXINCg0KYGBgDQpUaGUgZmlyc3QgbnVtYmVyIGlzIHRoZSBudW1iZXIgd2l0aCB0aGUgaGlnaGVzdCBtb2RlLCBhbmQgdGhlIHNlY29uZCBudW1iZXIgaXMgdGhlIGZyZXF1ZW5jeS4NCkxldCdzIGxvb2sgYXQgdGhlIHF1YWtlIGRhdGFzZXQNCmBgYHtyfQ0KUXRhYjwtdGFibGUocXVha2VzJG1hZykNClF0YWJbUXRhYj09bWF4KFF0YWIpXQ0KDQpgYGANClRoaXMgZ2l2ZXMgdXMgdGhlIG1hZ25pdHVkZSAoNC41KSB0aGF0IG9jY3VyZWQgdGhlIG1vc3QgKDEwNyB0aW1lcykNCg0KYGBge3J9DQojIHdoZW4gY29tcHV0aW5nIG1lYW4gd2l0aCBOYSBvciBOYW4gdmFsdWVzIHVzZSBuYS5ybT1UUlVFDQoNCnRlc3Q8LWMoMSw0LDYsTkEpDQp0ZXN0MTwtYygxLDQsNixOYU4pDQoNCm1lYW4odGVzdCkNCm1lYW4odGVzdDEpDQoNCg0KYGBgDQpgYGB7cn0NCiMgdXNlIG5hLnJtPXRydWUNCm1lYW4odGVzdCxuYS5ybT1UUlVFKQ0KbWVhbih0ZXN0MSxuYS5ybT1UUlVFKQ0KYGBgDQpUaGUgYmVuZWZpdCBvZiBbdGFwcGx5XQ0KU3VwcG9zZSB5b3Ugd2FudGVkIHRvIGNvbXB1dGUgc3VtbWFyeSBzdGF0cyANCg0KSW5zdGVhZCBvZiBkb2luZyB0aGlzOg0KDQpgYGB7cn0NCm1lYW4oY2hpY2t3dHMkd2VpZ2h0W2NoaWNrd3RzJGZlZWQ9PSJjYXNlaW4iXSkNCm1lYW4oY2hpY2t3dHMkd2VpZ2h0W2NoaWNrd3RzJGZlZWQ9PSJob3JzZWJlYW4iXSkNCm1lYW4oY2hpY2t3dHMkd2VpZ2h0W2NoaWNrd3RzJGZlZWQ9PSJsaW5zZWVkIl0pDQptZWFuKGNoaWNrd3RzJHdlaWdodFtjaGlja3d0cyRmZWVkPT0ibWVhdG1lYWwiXSkNCm1lYW4oY2hpY2t3dHMkd2VpZ2h0W2NoaWNrd3RzJGZlZWQ9PSJzb3liZWFuIl0pDQptZWFuKGNoaWNrd3RzJHdlaWdodFtjaGlja3d0cyRmZWVkPT0ic3VuZmxvd2VyIl0pDQoNCiAgDQpgYGANCllPdSBjb3VsZCB1c2UgdGhlIFt0YXBwbHldDQoNCmBgYHtyfQ0KdGFwcGx5KGNoaWNrd3RzJHdlaWdodCwgSU5ERVg9Y2hpY2t3dHMkZmVlZCxGVU49bWVhbikNCmBgYA0KRWFzaWVyIQ0KDQoNCjxoMz4gMTMuMi4yIENvdW50cyBQZXJjZW50YWdlcywgYW5kIFByb3BvcnRpb25zIDwvaDM+DQpJdCBpcyB1c2VmdWwgdG8ga25vdyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIA0KYGBge3J9DQojIGZpbmQgdGhlIG51bWJlciBvZiBlYWNoIHR5cGUgb2YgZmVlZHMNCnRhYmxlKGNoaWNrd3RzJGZlZWQpDQoNCiMgdGhlbiB0byBnZXQgdGhlIHByb3BvcnRpb25zDQp0YWJsZShjaGlja3d0cyRmZWVkKS9ucm93KGNoaWNrd3RzKQ0KYGBgDQoNCllPdSBjYW4gYWxzbyB1c2UgdGhlIGZhY3QgdGhhdCBUUlVFIGNvdW50cyBhcyAxIGFuZCBmYWxzZSBjb3VudHMgYXMgemVyby4NCmBgYHtyfQ0Kc3VtKGNoaWNrd3RzJGZlZWQ9PSJzb3liZWFuIikvbnJvdyhjaGlja3d0cykNCg0KYGBgDQoNCmBgYHtyfQ0KbWVhbihjaGlja3d0cyRmZWVkPT0ic295YmVhbiIpDQpgYGANClRoZW4gd2UgY2FuIGdldCBpbnRvIG1vcmUgY29tcGxpY2F0ZWQgY2FzZXMsIGxpa2Ugd2hhdCBpcyB0aGUgbWVhbiBvZiBzb3liZWFuIG9yIGhvcnNlYmVhbj8NCg0KYGBge3J9DQptZWFuKGNoaWNrd3RzJGZlZWQ9PSJzb3liZWFuInxjaGlja3d0cyRmZWVkPT0iaG9yc2ViZWFuIikNCmBgYA0KIA0KIFlvdSBjYW4gYWdhaW4gdXNlIHRoZSBbdGFwcGx5XQ0KIA0KIA0KDQpgYGB7cn0NCnRhcHBseShjaGlja3d0cyR3ZWlnaHQsSU5ERVg9Y2hpY2t3dHMkZmVlZCxGVU49ZnVuY3Rpb24oeCkgbGVuZ3RoKHgpL25yb3coY2hpY2t3dHMpKQ0KYGBgDQpZb3UgY2FuIHVzZSB0aGUgW3JvdW5kXSBmdW5jdGlvbiB0byByb3VuZCBvdXQgdGhlIG51bWJlcnMNCmBgYHtyfQ0KDQpyb3VuZCh0YWJsZShjaGlja3d0cyRmZWVkKS9ucm93KGNoaWNrd3RzKSxkaWdpdHMgPSAzKQ0KYGBgDQo8aDM+IDEzLjIuMyBRdWFydGlsZXMsIFBlcmNlbnRpbGVzLCBhbmQgdGhlIDUgbnVtYmVyIHN1bW1hcnkgPC9oMz4NCkEgcXVhbnRpbGUgaXMgYSB2YWx1ZSBjb21wdXRlZCBmcm9tIGEgY29sbGVjdGlvbiBvZiBudW1lcmljIG1lYXN1cmVtZW50cyB0aGF0IGluZGljYXRlcyBhbiBvYnNlcnZhdGlvbidzIHJhbmsgd2hlbiBjb21wYXJlZCB0byBhbGwgdGhlIG90aGVyIHByZXNlbnQgb2JzZXJ2YXRpb25zLg0KPC9icj4NCg0KUXVhbnRpbGVzIGRpdmlkZSB0aGUgb2JzZXJ2YXRpb25zIGluIGEgc2FtcGxlIGluIHRoZSBzYW1lIHdheS4gPC9icj4NClNvIHRoZSBtZWRpYW4gaXMgYSBxdWFudGlsZSBhcyBpdCBkaXZpZGVzIHRoZSBzYW1wbGVzIGludG8gMi4gPC9icj4NCkl0IGNhbiBhbHNvIGJlIGNhbGxlZCBwZXJjZW50aWxlIGFzIGluIHRoZSBwVEggcXVhbnRpbGUgaXMgZXF1aXZhbGVudCB0byAxMDAgeCBwVEggcGVyY2VudGlsZS48L3A+DQoNCmBgYHtyfQ0KIyByZXZpZXcgdGhlIGNvbnRlbnRzIG9mIHhkYXRhDQp4ZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBnZXQgdGhlIHF1YW50aWxlDQpxdWFudGlsZSh4ZGF0YSxwcm9iPTAuOCkNCmBgYA0KYGBge3J9DQpxdWFudGlsZSh4ZGF0YSxwcm9iPWMoMCwwLjI1LC41LC43NSwxKSkNCmBgYA0KVGhlIGFib3ZlIGlzIGNhbGxlZCB0aGUgJ2ZpdmUgbnVtYmVyIHN1bW1hcnknIG9mIHhkYXRhLg0KDQpgYGB7cn0NCnN1bW1hcnkoeGRhdGEpDQpgYGANCmBgYHtyfQ0KIyBjb21wdXRlIHRoZSBsb3dlciBhbmQgdXBwZXIgcXVhcnRpbGVzIG9mIHRoZSB3ZWlnaHRzIG9mIHRoZSBjaGlja3MgaW4gdGhlIGNoaWNrd3RzLg0KcXVhbnRpbGUoY2hpY2t3dHMkd2VpZ2h0LCBwcm9iPWMoMC4yNSwuNzUpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjb21wdXRlIHVzaW5nIHF1YWtlIGRhdGENCnN1bW1hcnkocXVha2VzJG1hZ1txdWFrZXMkZGVwdGg8NDAwXSkNCmBgYA0KDQo8aDM+IDEzLjIuNCBTcHJlYWQ6IFZhcmlhbmNlLCBTdGFuZGFyZCBEZXZpYXRpb24sIGFuZCBJbnRlcnF1YXJ0aWxlIFJhbmdlIDwvaDM+DQpTcHJlYWQgbWVhc3VyZXMgaG93IGRpc3BlcnNlZCB5b3VyIGRhdGEgYXJlLg0KDQpgYGB7cn0NCnhkYXRhDQp5ZGF0YT1jKDEsNC40LDEsMywyLDIuMiwyLDcpDQp5ZGF0YQ0KbWVhbih4ZGF0YSkNCm1lYW4oeWRhdGEpDQpgYGANCk5PdGljZSB0aGF0IGJvdGggeGRhdGEgYW5kIHlkYXRhIGhhdmUgdGhlIHNhbWUgbWVhbi4NCkxldCB1cyB2aXN1YWxpemUgdGhlbSBieSBwbG90dGluZyANCg0KYGBge3J9DQpwbG90KHhkYXRhLHR5cGU9Im4iLCB4bGFiPSIiLCB5bGFiPSJkYXRhIHZlY3RvciIsIHlheHQ9Im4iLGJ0eT0ibiIpDQphYmxpbmUoaD1jKDMsMy41KSxsdHk9MiwgY29sPSJncmF5IikNCmFibGluZSh2PTIuODI1LGx3ZD0yLGx0eT0zKQ0KdGV4dChjKDAuOCwwLjgpLGMoMywzLjUpLGxhYmVscz1jKCJ4IiwieSIpKQ0KcG9pbnRzKGppdHRlcihjKHhkYXRhLHlkYXRhKSksYyhyZXAoMyxsZW5ndGgoeGRhdGEpKSxyZXAoMy41LGxlbmd0aCh5ZGF0YSkpKSkNCmBgYA0KDQpjb21wYXJpbmcgdHdvIGh5cG90aGV0aWNhbCBkYXRhIHZlY3RvcnMgdGhhdCBzaGFyZSBhbiBpZGVudGljYWwgbWVhbi4gPC9icj4NClZhcmlhbmNlIG1lYXN1cmVzIHRoZSBkZWdyZWUgb2YgdGhlIHNwcmVhZCBvZiBudW1lcmljIG9ic2VydmF0aW9ucyBhcm91ZG4gdGhlaXIgYXJpdGhtZXRpYyBtZWFuLjwvYnI+DQpTdGFuZGFyZCBkZXZpYXRpb24gaXMgdGhlIHNxdWFyZSByb290IG9mIHRoZSB2YXJpYW5jZS4NCkludGVycXVhcnRpbGUgcmFuZ2UgKElRUikgbWVhc3VyZXMgdGhlIHdpZHRoIG9mIHRoZSBtaWRkbGUgNTAgcGVyY2VudCBvZiB0aGUgZGF0YSB0aGF0IGxpZSB3aXRoaW4gYSAyNSBwZXJjZW50IHF1YXJ0aWxlIG9uIGVpdGhlciBzaWRlIG9mIHRoZSBtZWRpYW4uIEFzIHN1Y2ggaXQgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdXBwZXIgYW5kIGxvd2VyIHF1YXJ0aWxlcyBvZiB5b3VyIGRhdGEuIA0KDQpgYGB7cn0NCnZhcih4ZGF0YSkNCnNkKHhkYXRhKQ0KSVFSKHhkYXRhKQ0KYGBgDQpgYGB7cn0NCiMgY29uZmlybSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYW5jZSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uDQpzcXJ0KHZhcih4ZGF0YSkpDQphcy5udW1lcmljKHF1YW50aWxlKHhkYXRhLDAuNzUpLXF1YW50aWxlKHhkYXRhLDAuMjUpKQ0KDQpgYGANCjAuOTUyODE1NCBpcyBhbHNvIHRoZSB2YWx1ZSBmb3Igc3RhbmRhcmQgZGV2aWF0aW9uLiA8L2JyPg0KMS4yNSBpcyBhbHNvIHRoZSB2YWx1ZSBvZiB0aGUgSVFSIA0KDQpOb3cgY29tcHV0ZSBmb3IgeWRhdGENCg0KYGBge3J9DQpzZCh5ZGF0YSkNCklRUih5ZGF0YSkNCnZhcih5ZGF0YSkNCmBgYA0KVGhlc2UgY29uZmlybSB0aGF0IHRoZSB5ZGF0YSByZXN1bHRzIGFyZSBtb3JlIHNwcmVhZG91dCBmcm9tIHRoZSBtZWFuICh0aGUgdmVydGljYWwgZG90dGVkIGxpbmUgaW4gdGhlIHBsb3QgYWJvdmUpDQoNCmBgYHtyfQ0KIyB3aGF0IGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgY2hpY2t3dHM/DQpzZChjaGlja3d0cyR3ZWlnaHQpDQpgYGANClRoaXMgbWVhbnMgdGhhdCBlYWNoIGNoaWNrIGlzIG9uIHRoZSBhdmVyYWdlIDc4LjA3MzcgZ3JhbXMgYXdheSBmcm9tIHRoZSBtZWFuIHdlaWdodC4NCg0KDQo8aDM+IDEzLjIuNSBDb3ZhcmlhbmNlIGFuZCBDb3JyZWxhdGlvbiA8L2gzPg0KVXNlIGNvcnJlbGF0aW9uIHRvIGFuYWx5c2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBudW1lcmljIHZhcmlhYmxlcyB0byBhc3Nlc3MgdHJlbmRzLjwvYnI+DQpDb3ZhcmlhbmNlIGV4cHJlc3NlcyBob3cgbXVjaCB0b3cgbnVtZXJpYyB2YXJpYWJsZXMgImNoYW5nZSB0b2dldGhlciI8L2JyPg0KUG9zaXR2ZSByZXN1bHRzIG1lYW4gdGhhdCB0aGV5IGFyZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgKHRoZSBmaWd1cmVzIG1vdmUgaW4gdGhlIHNhbWUgZGlyZWN0aW9uKTwvYnI+DQpOZWdhdGl2ZSByZXN1bHRzIG1lYW4gdGhhdCB0aGV5IGFyZSBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgKHRoZSBmaWd1cmVzIG1vdmUgaW4gb3Bwb3NpdGUgZGlyZWN0aW9uKTwvYnI+DQoNClRoZSByZWxhdGlvbnNoaXAgZ3Jvd3Mgd2Vha2VyIHRoZSBuZWFyZXIgaXQgaXMgdG8gemVyby4NCkNvcnJlbGF0aW9uIGFsbG93cyB5b3UgdG8gaW50ZXJwcmV0IHRoZSBjb3ZhcmlhbmNlIGZ1cmh0ZXIgYnkgaWRlbnRpZnlpbmcgYm90aCB0aGUgZGlyZWN0aW9uIGFuZCB0aGUgc3RyZW5ndGggb2YgYW55IGFzc29jaWF0aW9uLg0KDQpgYGB7cn0NCiMgcmVjYWxsIHhkYXRhIGFuZCB5ZGF0YQ0KeGRhdGENCnlkYXRhDQoNCmBgYA0KDQpgYGB7cn0NCmNvdih4ZGF0YSx5ZGF0YSkNCmNvdih4ZGF0YSx5ZGF0YSkvKHNkKHhkYXRhKSpzZCh5ZGF0YSkpDQpjb3IoeGRhdGEseWRhdGEpDQpgYGANClBsb3R0aW5nLi4uDQoNCmBgYHtyfQ0KcGxvdCh4ZGF0YSx5ZGF0YSxwY2g9MTMsY2V4PTEuNSkNCmBgYA0KTG9va2luZyBhdCB0aGUgcXVha2UgZGF0YXNldCBhbmQgcGxvdHRpbmcgdGhlbQ0KDQpgYGB7cn0NCnBsb3QocXVha2VzJG1hZyxxdWFrZXMkc3RhdGlvbnMseGxhYj0ibWFnbml0dWRlIix5bGFiPSJObyBvZiBzdGF0aW9ucyIpDQpgYGANCmBgYHtyfQ0KY292KHF1YWtlcyRtYWcscXVha2VzJHN0YXRpb25zKQ0KYGBgDQpgYGB7cn0NCmNvcihxdWFrZXMkbWFnLHF1YWtlcyRzdGF0aW9ucykNCmBgYA0KUmVtZW1iZXIgY29ycmVsYXRpb24gYWxsb3dzIHlvdSB0byBtZWFzdXJlIGFzc29jaWF0aW9uLiBOb3QgY2F1c2F0aW9uLg0KDQo8aDM+IDEzLjIuNiBPdXRsaWVycyA8L2gzPg0KT3V0bGllcnMgYXJlIG9ic2VydmF0aW9ucyB0aGF0IGRvZXMgbm90IGFwcGVhciB0byBmaXQgd2l0aCB0aGUgcmVzdCBvZiBodGUgZGF0YS4gSXQgdXN1YWxseSBwcmVzZW50cyBpdHNlbGYgYXMgZXh0cmVtZSB2YWx1ZXMuDQoNCmBgYHtyfQ0KIyBleGFtcGxlIG9mIG91dGxpZXJzIGJ5IHBsb3R0aW5nDQpmb288LWMoMC42LC0wLjYsLjEsLTAuMiwtMS4wLDAuNCwuMywtMS44LDEuMSw2LjApDQpmb28NCg0KYGBgDQpgYGB7cn0NCnBsb3QoZm9vLHJlcCgwLDEwLHlheHQ9Im4iLHlsYWI9IiIsYnR5PSJuIixjZXg9MixjZXguYXhpcz0xLjUsY2V4LmxhYj0xLjUpKQ0KYWJsaW5lKGg9MCxjb2w9ImdyYXkiLGx0eT0yKQ0KYXJyb3dzKDUsMC41LDUuOSwwLjEsbHdkPTIpDQp0ZXh0KDUsMC43LGxhYmVscz0ib3V0bGllcj8iLGNleD0zKQ0KYGBgDQoNCmBgYHtyfQ0KIyBiaXZhcmF0ZSBleGFtcGxlDQpiYXI8LWMoMC4xLC4zLDEuMywuNiwuMiwtMS43LC44LC45LC0uOCwtMSkNCmJhejwtYygtMC4zLC45LDIuOCwyLjMsMS4yLC00LjEsLS40LDQuMSwtMi4zLC0xMDApDQpwbG90KGJhcixiYXosYXhlcz1ULGNleD0yLGNleC5heGlzPTEuNSxjZXgubGFiPTEuNSkNCmFycm93cygtLjUsLTgwLC0uOTQsLTk3LGx3ZD0yKQ0KdGV4dCgtMC40NSwtNzQsbGFiZWxzPSJvdXRsaWVyPyIsY2V4PTMpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K