Load lesson dependencies..

library(swirl)
#install_from_swirl("Exploratory Data Analysis")
library(ggplot2)
library(jpeg)


Slides for this and other Data Science courses may be found at github. If you care to use them, they must be downloaded as a zip file and viewed locally. This lesson corresponds to 04_ExploratoryAnalysis/exploratoryGraphs.


In this lesson, we’ll discuss why graphics are an important tool for data scientists and the special role that exploratory graphs play in the field.

The following would be a good reason to use graphics in data science:

  1. To understand data properties
  2. To suggest modeling strategies
  3. To find patterns in data

So graphics give us some visual form of data, and since our brains are very good at seeing patterns, graphs give us a compact way to present data and find or display any pattern that may be present.

Exploratory graphs serve mostly the same functions as graphs. They help us find patterns in data and understand its properties. They suggest modeling strategies and help to debug analyses. We DON’T use exploratory graphs to communicate results.

Instead, exploratory graphs are the initial step in an investigation, the “quick and dirty” tool used to point the data scientist in a fruitful direction. A scientist might need to make a lot of exploratory graphs in order to develop a personal understanding of the problem being studied. Plot details such as axes, legends, color and size are cleaned up later to convey more information in an aesthetically pleasing way.

To demonstrate these ideas, we’ve copied some data for you from the U.S. Environmental Protection Agency (EPA) which sets national ambient air quality standards for outdoor air pollution. These Standards say that for fine particle pollution (PM2.5), the “annual mean, averaged over 3 years” cannot exceed 12 micro grams per cubic meter. We stored the data from the U.S. EPA web site in the data frame pollution. Use the R function head to see the first few entries of pollution.

head(pollution)

We see right away that there’s at least one county exceeding the EPA’s standard of 12 micrograms per cubic meter. What else do we see?

We see 5 columns of data. The pollution count is in the first column labeled pm25. We’ll work mostly with that. The other 4 columns are a fips code indicating the state (first 2 digits) and county (last 3 digits) with that count, the associated region (east or west), and the longitude and latitude of the area. Now run the R command dim with pollution as an argument to see how long the table is.

dim(pollution)
[1] 576   5

So there are 576 entries in pollution. We’d like to investigate the question “Are there any counties in the U.S. that exceed that national standard (12 micro grams per cubic meter) for fine particle pollution?” We’ll look at several one dimensional summaries of the data to investigate this question.

The first technique uses the R command summary, a 5-number summary which returns 6 numbers. Run it now with the pm25 column of pollution as its argument. Recall that the construct for this is pollution$pm25.

summary(pollution$pm25)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  3.383   8.549  10.047   9.836  11.356  18.441 

This shows us basic info about the pm25 data, namely its Minimum (0 percentile) and Maximum (100 percentile) values, and three Quartiles of the data. These last indicate the pollution measures at which 25%, 50%, and 75% of the counties fall below. In addition to these 5 numbers we see the Mean or average measure of particulate pollution across the 576 counties.

Half the measured counties have a pollution level less than or equal to what number of micrograms per cubic meter?

  1. 10.050
  2. 11.360
  3. 9.836
  4. 8.549

To save you a lot of typing we’ve saved off pollution$pm25 for you in the variable ppm. You can use ppm now in place of the longer expression. Try it now as the argument of the R command quantile. See how the results look a lot like the results of the output of the summary command.

quantile(ppm)
       0%       25%       50%       75%      100% 
 3.382626  8.548799 10.046697 11.356012 18.440731 

See how the results are similar to those returned by summary? quantile gives the quartiles, right?

What is the one value missing from this quantile output that summary gave you?

  1. the mean
  2. the median
  3. the maximum value
  4. the minimum value

Now we’ll plot a picture, specifically a boxplot. Run the R command boxplot with ppm as an input. Also specify the color parameter col equal to "blue".

boxplot(ppm, col = "blue")

The boxplot shows us the same quartile data that summary and quantile did. The lower and upper edges of the blue box respectively show the values of the 25% and 75% quantiles.

What do you think the horizontal line inside the box represents?

  1. the minimum value
  2. the median
  3. the maximum value
  4. the mean

The “whiskers” of the box (the vertical lines extending above and below the box) relate to the range parameter of boxplot, which we let default to the value 1.5 used by R. The height of the box is the interquartile range, the difference between the 75th and 25th quantiles. In this case that difference is 2.8. The whiskers are drawn to be a length of range x 2.8 or 1.5*2.8. This shows us roughly how many, if any, data points are outliers, that is, beyond this range of values.

Note that boxplot is part of R’s base plotting package. A nice feature that this package provides is its ability to overlay features. That is, you can add to (annotate) an existing plot.

To see this, run the R command abline with the argument h equal to 12. Recall that 12 is the EPA standard for air quality.

boxplot(ppm, col = "blue")
abline(h=12)

What do you think this command did?

  1. drew a horizontal line at 12
  2. hid 12 random data points
  3. drew a vertical line at 12
  4. nothing

So abline “adds one or more straight lines through the current plot.” We see from the plot that the bulk of the measured counties comply with the standard since they fall under the line marking that standard.


Now use the R command hist (another function from the base package) with the argument ppm. Specify the color parameter col equal to "green". This will plot a histogram of the data.

hist(ppm, col = "green")

The histogram gives us a little more detailed information about our data, specifically the distribution of the pollution counts, or how many counties fall into each bucket of measurements.

What are the most frequent pollution counts?

  1. between 12 and 14
  2. under 5
  3. between 6 and 8
  4. between 9 and 12


Now run the R command rug with the argument ppm.

hist(ppm, col = "green")
rug(ppm)

This one-dimensional plot, with its grayscale representation, gives you a little more detailed information about how many data points are in each bucket and where they lie within the bucket. It shows (through density of tick marks) that the greatest concentration of counties has between 9 and 12 micrograms per cubic meter just as the histogram did.

To illustrate this a little more, we’ve defined for you two vectors, high and low, containing pollution data of high (greater than 15) and low (less than 5) values respectively. Look at low now and see how it relates to the output of rug.

low
 [1] 3.494351 4.186090 4.917140 4.504539 4.793644 4.601408 4.195688 4.625279 4.460193 4.978397 4.324736
[12] 4.175901 3.382626 4.132739 4.955570 4.565808

It confirms that there are two data points between 3 and 4 and many between 4 and 5. Now look at high.

high
[1] 16.19452 15.80378 18.44073 16.66180 15.01573 17.42905 16.25190 16.18358

Again, we see one data point greater than 18, one between 17 and 18, several between 16 and 17 and two between 15 and 16, verifying what rug indicated.

Now rerun hist with 3 arguments, ppm as its first, col equal to "green", and the argument breaks equal to 100.

hist(ppm, col="green", breaks = 100)

What do you think the breaks argument specifies in this case?

  1. the number of stars in the sky
  2. the number of buckets to split the data into
  3. the number of counties exceeding the EPA standard
  4. the number of data points to graph

So this histogram with more buckets is not nearly as smooth as the preceding one. In fact, it’s a little too noisy to see the distribution clearly. When you’re plotting histograms you might have to experiment with the argument breaks to get a good idea of your data’s distribution. For fun now, rerun the R command rug with the argument ppm.

hist(ppm, col="green", breaks = 100)
rug(ppm)

See how rug works with the existing plot? It automatically adjusted its pocket size to that of the last plot plotted.

Now rerun hist with ppm as the data and col equal to "green".

hist(ppm, col = "green")

Now run the command abline with the argument v equal to 12 and the argument lwd equal to 2.

hist(ppm, col = "green")
abline(v=12, lwd=2)

See the vertical line at 12? Not very visible, is it, even though you specified a line width of 2? Run abline with the argument v equal to median(ppm), the argument col equal to "magenta", and the argument lwd equal to 4.

hist(ppm, col = "green")
abline(v=median(ppm), col="magenta", lwd=4)

Better, right? Thicker and more of a contrast in color. This shows that although the median (50%) is below the standard, there are a fair number of counties in the U.S that have pollution levels higher than the standard.

Now recall that our pollution data had 5 columns of information. So far we’ve only looked at the pm25 column. We can also look at other information. To remind yourself what’s there run the R command names with pollution as the argument.

names(pollution)
[1] "pm25"      "fips"      "region"    "longitude" "latitude" 

Longitude and latitude don’t sound interesting, and each fips is unique since it identifies states (first 2 digits) and counties (last 3 digits). Let’s look at the region column to see what’s there. Run the R command table on this column. Use the construct pollution$region. Store the result in the variable reg.

reg <- table(pollution$region)

Look at reg now.

reg

east west 
 442  134 

Lot more counties in the east than west. We’ll use the R command barplot (another type of one-dimensional summary) to plot this information. Call barplot with reg as its first argument, the argument col equal to "wheat", and the argument main equal to the string "Number of Counties in Each Region".

barplot(reg, col="wheat", main="Number of Counties in Each Region")

What do you think the argument main specifies?

  1. the title of the graph
  2. the x axis label
  3. I can’t tell
  4. the y axis label


So we’ve seen several examples of one-dimensional graphs that summarize data. Two dimensional graphs include scatterplots, multiple graphs which we’ll see more examples of, and overlayed one-dimensional plots which the R packages such as lattice and ggplot2 provide.

Some graphs have more than two-dimensions. These include overlayed or multiple two-dimensional plots and spinning plots. Some three-dimensional plots are tricky to understand so have limited applications. We’ll see some examples now of more complicated graphs, in particular, we’ll show two graphs together.

First we’ll show how R, in one line and using base plotting, can display multiple boxplots. We simply specify that we want to see the pollution data as a function of region. We know that our pollution data characterized each of the 576 entries as belonging to one of two regions (east and west).

We use the R formula y ~ x to show that y (in this case pm25) depends on x (region). Since both come from the same data frame (pollution) we can specify a data argument set equal to pollution. By doing this, we don’t have to type pollution$pm25 (or ppm) and pollution$region. We can just specify the formula pm25~region. Call boxplot now with this formula as its argument, data equal to pollution, and col equal to "red".

boxplot(pm25~region, pollution, col="red")

Two for the price of one!

Similarly we can plot multiple histograms in one plot, though to do this we have to use more than one R command. First we have to set up the plot window with the R command par which specifies how we want to lay out the plots, say one above the other. We also use par to specify margins, a 4-long vector which indicates the number of lines for the bottom, left, top and right.

Type the R command par(mfrow=c(2,1),mar=c(4,4,2,1)) now. Don’t expect to see any new result.

par(mfrow=c(2,1),mar=c(4,4,2,1))

So we set up the plot window for two rows and one column with the mfrow argument. The mar argument set up the margins.

Before we plot the histograms let’s explore the R command subset which, not surprisingly, “returns subsets of vectors, matrices or data frames which meet conditions”. We’ll use subset to pull off the data we want to plot.

Call subset now with pollution as its first argument and a boolean expression testing region for equality with the string "east". Put the result in the variable east.

east <- subset(pollution, region == "east")

Use head to look at the first few entries of east.

head(east)

So east holds more information than we need. We just want to plot a histogram with the pm25 portion. Call hist now with the pm25 portion of east as its first argument and col equal to "green" as its second.

hist(east$pm25, col = "green")

See? The command par told R we were going to have one column with 2 rows, so it placed this histogram in the top position.

Now, here’s a challenge for you. Plot the histogram of the counties from the west using just one R command. Let the appropriate subset command (with the pm25 portion specified) be the first argument and col (equal to "green") the second. To cut down on your typing, use the up arrow key to get your last command and replace "east" with the subset command. Make sure the boolean argument checks for equality between region and "west".

hist(subset(pollution, region == "west")$pm25, col = "green")

See how R does all the labeling for you? Notice that the titles are different since we used different commands for the two plots. Let’s look at some scatter plots now.


Scatter plots are two-dimensional plots which show the relationship between two variables, usually x and y. Let’s look at a scatterplot showing the relationship between latitude and the pm25 data. We’ll use plot, a function from R’s base plotting package.

We’ve seen that we can use a function call as an argument when calling another function. We’ll do this again when we call plot with the arguments latitude and pm25 which are both from our data frame pollution. We’ll call plot from inside the R command with which evaluates “an R expression in an environment constructed from data”. We’ll use pollution as the first argument to with and the call to plot as the second. This allows us to avoid typing "pollution$" before the arguments to plot, so it saves us some typing and adds to your base of R knowledge. Try this now.

with(pollution, plot(latitude, pm25))

Note that the first argument is plotted along the x-axis and the second along the y. Now use abline to add a horizontal line at 12. Use two additional arguments, lwd equal to 2 and lty also equal to 2. See what happens.

with(pollution, plot(latitude, pm25))
abline(h=12, lwd=2, lty=2)

See how lty=2 made the line dashed? Now let’s replot the scatterplot. This time, instead of using with, call plot directly with 3 arguments. The first 2 are pollution$latitude and ppm. The third argument, col, we’ll use to add color and more information to our plot. Set this argument (col) equal to pollution$region and see what happens.

plot(pollution$latitude, ppm, col=pollution$region)

We’ve got two colors on the map to distinguish between counties in the east and those in the west. Can we figure out which color is east and which west? See that the high (greater than 50) and low (less than 25) latitudes are both red. Latitudes indicate distance from the equator, so which half of the U.S. (east or west) has counties at the extreme north and south?

1: west 2: east

As before, use abline to add a horizontal line at 12. Use two additional arguments, lwd equal to 2 and lty also equal to 2.

plot(pollution$latitude, ppm, col=pollution$region)
abline(h=12, lwd=2, lty=2)

We see many counties are above the healthy standard set by the EPA, but it’s hard to tell overall, which region, east or west, is worse.

Let’s plot two scatterplots distinguished by region.

As we did with multiple histograms, we first have to set up the plot window with the R command par. This time, let’s plot the scatterplots side by side (one row and two columns). We also need to use different margins. Type the R command par(mfrow = c(1, 2), mar = c(5, 4, 2, 1)) now. Don’t expect to see any new result.

par(mfrow = c(1, 2), mar = c(5, 4, 2, 1))

For the first scatterplot, on the left, we’ll plot the latitudes and pm25 counts from the west. We already pulled out the information for the counties in the east. Let’s now get the information for the counties from the west. Create the variable west by using the subset command with pollution as the first argument and the appropriate boolean as the second.

west <- subset(pollution, region == "west")

Now call plot with three arguments. These are west$latitude (x-axis), west$pm25 (y-axis), and the argument main equal to the string "West" (title). Do this now.

plot(west$latitude, west$pm25, main = "West")

For the second scatterplot, on the right, we’ll plot the latitudes and pm25 counts from the east.

plot(east$latitude, east$pm25, main = "East")

See how R took care of all the details for you? Nice, right? It looks like there are more dirty counties in the east but the extreme dirt (greater than 15) is in the west.


Let’s summarize and review.

Which of the following characterizes exploratory plots?

  1. slow and steady
  2. quick and dead
  3. slow and clean
  4. quick and dirty

True or false? Plots let you summarize the data (usually graphically) and highlight any broad features

  1. True
  2. False

Which of the following do plots NOT do?

  1. Suggest modeling strategies for the “next step”
  2. Summarize the data (usually graphically) and highlight any broad features
  3. Conclude that you are ALWAYS right
  4. Explore basic questions and hypotheses (and perhaps rule them out)

Congrats! You’ve concluded exploring this lesson on graphics. We hope you didn’t find it too quick or dirty.




END



LS0tDQp0aXRsZTogIkV4cGxvcmF0b3J5IEdyYXBocyINCnN1YnRpdGxlOiAiU3dpcmwgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBMZXNzb24gMiINCmF1dGhvcjogIlJvZ2VyIEQuIFBlbmcgKGNvbXBpbGVkIGFuZCBlZGl0ZWQgYnk6IE51bm5vIE51Z3JvaG8pIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPC9icj4NCg0KTG9hZCBsZXNzb24gZGVwZW5kZW5jaWVzLi4NCmBgYHtyfQ0KbGlicmFyeShzd2lybCkNCiNpbnN0YWxsX2Zyb21fc3dpcmwoIkV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShqcGVnKQ0KYGBgDQoNCjwvYnI+DQoNCipTbGlkZXMgZm9yIHRoaXMgYW5kIG90aGVyIERhdGEgU2NpZW5jZSBjb3Vyc2VzIG1heSBiZSBmb3VuZCBhdCBbZ2l0aHViXShodHRwczovL2dpdGh1Yi5jb20vRGF0YVNjaWVuY2VTcGVjaWFsaXphdGlvbi9jb3Vyc2VzLykuIElmIHlvdSBjYXJlIHRvIHVzZSB0aGVtLCB0aGV5IG11c3QgYmUgZG93bmxvYWRlZCBhcyBhIHppcCBmaWxlIGFuZCB2aWV3ZWQgbG9jYWxseS4gVGhpcyBsZXNzb24gY29ycmVzcG9uZHMgdG8gMDRfRXhwbG9yYXRvcnlBbmFseXNpcy9leHBsb3JhdG9yeUdyYXBocy4qDQoNCjwvYnI+DQoNCkluIHRoaXMgbGVzc29uLCB3ZSdsbCBkaXNjdXNzIHdoeSBncmFwaGljcyBhcmUgYW4gaW1wb3J0YW50IHRvb2wgZm9yIGRhdGEgc2NpZW50aXN0cyBhbmQgdGhlIHNwZWNpYWwgcm9sZSB0aGF0IGV4cGxvcmF0b3J5IGdyYXBocyBwbGF5IGluIHRoZSBmaWVsZC4NCg0KVGhlIGZvbGxvd2luZyB3b3VsZCBiZSBhIGdvb2QgcmVhc29uIHRvIHVzZSBncmFwaGljcyBpbiBkYXRhIHNjaWVuY2U6DQoNCjEuIFRvIHVuZGVyc3RhbmQgZGF0YSBwcm9wZXJ0aWVzDQoyLiBUbyBzdWdnZXN0IG1vZGVsaW5nIHN0cmF0ZWdpZXMNCjMuIFRvIGZpbmQgcGF0dGVybnMgaW4gZGF0YQ0KDQpTbyBncmFwaGljcyBnaXZlIHVzIHNvbWUgdmlzdWFsIGZvcm0gb2YgZGF0YSwgYW5kIHNpbmNlIG91ciBicmFpbnMgYXJlIHZlcnkgZ29vZCBhdCBzZWVpbmcgcGF0dGVybnMsIGdyYXBocyBnaXZlIHVzIGEgY29tcGFjdCB3YXkgdG8gcHJlc2VudCBkYXRhIGFuZCBmaW5kIG9yIGRpc3BsYXkgYW55IHBhdHRlcm4gdGhhdCBtYXkgYmUgcHJlc2VudC4NCg0KRXhwbG9yYXRvcnkgZ3JhcGhzIHNlcnZlIG1vc3RseSB0aGUgc2FtZSBmdW5jdGlvbnMgYXMgZ3JhcGhzLiBUaGV5IGhlbHAgdXMgZmluZCBwYXR0ZXJucyBpbiBkYXRhIGFuZCB1bmRlcnN0YW5kIGl0cyBwcm9wZXJ0aWVzLiBUaGV5IHN1Z2dlc3QgbW9kZWxpbmcgc3RyYXRlZ2llcyBhbmQgaGVscCB0byBkZWJ1ZyBhbmFseXNlcy4gV2UgRE9OJ1QgdXNlIGV4cGxvcmF0b3J5IGdyYXBocyB0byBjb21tdW5pY2F0ZSByZXN1bHRzLg0KDQpJbnN0ZWFkLCBleHBsb3JhdG9yeSBncmFwaHMgYXJlIHRoZSBpbml0aWFsIHN0ZXAgaW4gYW4gaW52ZXN0aWdhdGlvbiwgdGhlICJxdWljayBhbmQgZGlydHkiIHRvb2wgdXNlZCB0byBwb2ludCB0aGUgZGF0YSBzY2llbnRpc3QgaW4gYSBmcnVpdGZ1bCBkaXJlY3Rpb24uIEEgc2NpZW50aXN0IG1pZ2h0IG5lZWQgdG8gbWFrZSBhIGxvdCBvZiBleHBsb3JhdG9yeSBncmFwaHMgaW4gb3JkZXIgdG8gZGV2ZWxvcCBhIHBlcnNvbmFsIHVuZGVyc3RhbmRpbmcgb2YgdGhlIHByb2JsZW0gYmVpbmcgc3R1ZGllZC4gUGxvdCBkZXRhaWxzIHN1Y2ggYXMgYXhlcywgbGVnZW5kcywgY29sb3IgYW5kIHNpemUgYXJlIGNsZWFuZWQgdXAgbGF0ZXIgdG8gY29udmV5IG1vcmUgaW5mb3JtYXRpb24gaW4gYW4gYWVzdGhldGljYWxseSBwbGVhc2luZyB3YXkuDQoNClRvIGRlbW9uc3RyYXRlIHRoZXNlIGlkZWFzLCB3ZSd2ZSBjb3BpZWQgc29tZSBkYXRhIGZvciB5b3UgZnJvbSB0aGUgVS5TLiBFbnZpcm9ubWVudGFsIFByb3RlY3Rpb24gQWdlbmN5IChFUEEpIHdoaWNoIHNldHMgbmF0aW9uYWwgYW1iaWVudCBhaXIgcXVhbGl0eSBzdGFuZGFyZHMgZm9yIG91dGRvb3IgYWlyIHBvbGx1dGlvbi4gVGhlc2UgU3RhbmRhcmRzIHNheSB0aGF0IGZvciBmaW5lIHBhcnRpY2xlIHBvbGx1dGlvbiAoUE0yLjUpLCB0aGUgImFubnVhbCBtZWFuLCBhdmVyYWdlZCBvdmVyIDMgeWVhcnMiIGNhbm5vdCBleGNlZWQgMTIgbWljcm8gZ3JhbXMgcGVyIGN1YmljIG1ldGVyLiBXZSBzdG9yZWQgdGhlIGRhdGEgZnJvbSB0aGUgVS5TLiBFUEEgd2ViIHNpdGUgaW4gdGhlIGRhdGEgZnJhbWUgcG9sbHV0aW9uLiBVc2UgdGhlIFIgZnVuY3Rpb24gYGhlYWRgIHRvIHNlZSB0aGUgZmlyc3QgZmV3IGVudHJpZXMgb2YgcG9sbHV0aW9uLg0KDQpgYGB7cn0NCmhlYWQocG9sbHV0aW9uKQ0KYGBgDQoNCldlIHNlZSByaWdodCBhd2F5IHRoYXQgdGhlcmUncyBhdCBsZWFzdCBvbmUgY291bnR5IGV4Y2VlZGluZyB0aGUgRVBBJ3Mgc3RhbmRhcmQgb2YgMTIgbWljcm9ncmFtcyBwZXIgY3ViaWMgbWV0ZXIuIFdoYXQgZWxzZSBkbyB3ZSBzZWU/DQoNCldlIHNlZSA1IGNvbHVtbnMgb2YgZGF0YS4gVGhlIHBvbGx1dGlvbiBjb3VudCBpcyBpbiB0aGUgZmlyc3QgY29sdW1uIGxhYmVsZWQgYHBtMjVgLiBXZSdsbCB3b3JrIG1vc3RseSB3aXRoIHRoYXQuIFRoZSBvdGhlciA0IGNvbHVtbnMgYXJlIGEgZmlwcyBjb2RlIGluZGljYXRpbmcgdGhlIHN0YXRlIChmaXJzdCAyIGRpZ2l0cykgYW5kIGNvdW50eSAobGFzdCAzIGRpZ2l0cykgd2l0aCB0aGF0IGNvdW50LCB0aGUgYXNzb2NpYXRlZCByZWdpb24gKGVhc3Qgb3Igd2VzdCksIGFuZCB0aGUgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSBvZiB0aGUgYXJlYS4gTm93IHJ1biB0aGUgUiBjb21tYW5kIGBkaW1gIHdpdGggcG9sbHV0aW9uIGFzIGFuIGFyZ3VtZW50IHRvIHNlZSBob3cgbG9uZyB0aGUgdGFibGUgaXMuDQoNCmBgYHtyfQ0KZGltKHBvbGx1dGlvbikNCmBgYA0KDQpTbyB0aGVyZSBhcmUgNTc2IGVudHJpZXMgaW4gcG9sbHV0aW9uLiBXZSdkIGxpa2UgdG8gaW52ZXN0aWdhdGUgdGhlIHF1ZXN0aW9uICJBcmUgdGhlcmUgYW55IGNvdW50aWVzIGluIHRoZSBVLlMuIHRoYXQgZXhjZWVkIHRoYXQgbmF0aW9uYWwgc3RhbmRhcmQgKDEyIG1pY3JvIGdyYW1zIHBlciBjdWJpYyBtZXRlcikgZm9yIGZpbmUgcGFydGljbGUgcG9sbHV0aW9uPyIgV2UnbGwgbG9vayBhdCBzZXZlcmFsIG9uZSBkaW1lbnNpb25hbCBzdW1tYXJpZXMgb2YgdGhlIGRhdGEgdG8gaW52ZXN0aWdhdGUgdGhpcyBxdWVzdGlvbi4NCg0KVGhlIGZpcnN0IHRlY2huaXF1ZSB1c2VzIHRoZSBSIGNvbW1hbmQgYHN1bW1hcnlgLCBhIDUtbnVtYmVyIHN1bW1hcnkgd2hpY2ggcmV0dXJucyA2IG51bWJlcnMuIFJ1biBpdCBub3cgd2l0aCB0aGUgYHBtMjVgIGNvbHVtbiBvZiBwb2xsdXRpb24gYXMgaXRzIGFyZ3VtZW50LiBSZWNhbGwgdGhhdCB0aGUgY29uc3RydWN0IGZvciB0aGlzIGlzIGBwb2xsdXRpb24kcG0yNWAuDQoNCmBgYHtyfQ0Kc3VtbWFyeShwb2xsdXRpb24kcG0yNSkNCmBgYA0KDQpUaGlzIHNob3dzIHVzIGJhc2ljIGluZm8gYWJvdXQgdGhlICpwbTI1KiBkYXRhLCBuYW1lbHkgaXRzIE1pbmltdW0gKDAgcGVyY2VudGlsZSkgYW5kIE1heGltdW0gKDEwMCBwZXJjZW50aWxlKSB2YWx1ZXMsIGFuZCB0aHJlZSBRdWFydGlsZXMgb2YgdGhlIGRhdGEuIFRoZXNlIGxhc3QgaW5kaWNhdGUgdGhlIHBvbGx1dGlvbiBtZWFzdXJlcyBhdCB3aGljaCAyNSUsIDUwJSwgYW5kIDc1JSBvZiB0aGUgY291bnRpZXMgZmFsbCBiZWxvdy4gSW4gYWRkaXRpb24gdG8gdGhlc2UgNSBudW1iZXJzIHdlIHNlZSB0aGUgTWVhbiBvciBhdmVyYWdlIG1lYXN1cmUgb2YgcGFydGljdWxhdGUgcG9sbHV0aW9uIGFjcm9zcyB0aGUgNTc2IGNvdW50aWVzLg0KDQpIYWxmIHRoZSBtZWFzdXJlZCBjb3VudGllcyBoYXZlIGEgcG9sbHV0aW9uIGxldmVsIGxlc3MgdGhhbiBvciBlcXVhbCB0byB3aGF0IG51bWJlciBvZiBtaWNyb2dyYW1zIHBlciBjdWJpYyBtZXRlcj8NCg0KMS4gKioxMC4wNTAqKg0KMi4gMTEuMzYwDQozLiA5LjgzNg0KNC4gOC41NDkNCg0KVG8gc2F2ZSB5b3UgYSBsb3Qgb2YgdHlwaW5nIHdlJ3ZlIHNhdmVkIG9mZiBgcG9sbHV0aW9uJHBtMjVgIGZvciB5b3UgaW4gdGhlIHZhcmlhYmxlIGBwcG1gLiBZb3UgY2FuIHVzZSBgcHBtYCBub3cgaW4gcGxhY2Ugb2YgdGhlIGxvbmdlciBleHByZXNzaW9uLiBUcnkgaXQgbm93IGFzIHRoZSBhcmd1bWVudCBvZiB0aGUgUiBjb21tYW5kIGBxdWFudGlsZWAuIFNlZSBob3cgdGhlIHJlc3VsdHMgbG9vayBhIGxvdCBsaWtlIHRoZSByZXN1bHRzIG9mIHRoZSBvdXRwdXQgb2YgdGhlIGBzdW1tYXJ5YCBjb21tYW5kLg0KDQpgYGB7cn0NCnF1YW50aWxlKHBwbSkNCmBgYA0KDQpTZWUgaG93IHRoZSByZXN1bHRzIGFyZSBzaW1pbGFyIHRvIHRob3NlIHJldHVybmVkIGJ5IGBzdW1tYXJ5YD8gYHF1YW50aWxlYCBnaXZlcyB0aGUgcXVhcnRpbGVzLCByaWdodD8NCg0KV2hhdCBpcyB0aGUgb25lIHZhbHVlIG1pc3NpbmcgZnJvbSB0aGlzIGBxdWFudGlsZWAgb3V0cHV0IHRoYXQgYHN1bW1hcnlgIGdhdmUgeW91Pw0KDQoxLiAqKnRoZSBtZWFuKioNCjIuIHRoZSBtZWRpYW4NCjMuIHRoZSBtYXhpbXVtIHZhbHVlDQo0LiB0aGUgbWluaW11bSB2YWx1ZQ0KDQpOb3cgd2UnbGwgcGxvdCBhIHBpY3R1cmUsIHNwZWNpZmljYWxseSBhIGJveHBsb3QuIFJ1biB0aGUgUiBjb21tYW5kIGBib3hwbG90YCB3aXRoIGBwcG1gIGFzIGFuIGlucHV0LiBBbHNvIHNwZWNpZnkgdGhlIGNvbG9yIHBhcmFtZXRlciBgY29sYCBlcXVhbCB0byBgImJsdWUiYC4NCg0KYGBge3J9DQpib3hwbG90KHBwbSwgY29sID0gImJsdWUiKQ0KYGBgDQoNClRoZSBgYm94cGxvdGAgc2hvd3MgdXMgdGhlIHNhbWUgcXVhcnRpbGUgZGF0YSB0aGF0IGBzdW1tYXJ5YCBhbmQgYHF1YW50aWxlYCBkaWQuIFRoZSBsb3dlciBhbmQgdXBwZXIgZWRnZXMgb2YgdGhlIGJsdWUgYm94IHJlc3BlY3RpdmVseSBzaG93IHRoZSB2YWx1ZXMgb2YgdGhlIDI1JSBhbmQgNzUlIHF1YW50aWxlcy4NCg0KV2hhdCBkbyB5b3UgdGhpbmsgdGhlIGhvcml6b250YWwgbGluZSBpbnNpZGUgdGhlIGJveCByZXByZXNlbnRzPw0KDQoxLiB0aGUgbWluaW11bSB2YWx1ZQ0KMi4gKip0aGUgbWVkaWFuKioNCjMuIHRoZSBtYXhpbXVtIHZhbHVlDQo0LiB0aGUgbWVhbg0KDQpUaGUgIndoaXNrZXJzIiBvZiB0aGUgYm94ICh0aGUgdmVydGljYWwgbGluZXMgZXh0ZW5kaW5nIGFib3ZlIGFuZCBiZWxvdyB0aGUgYm94KSByZWxhdGUgdG8gdGhlIHJhbmdlIHBhcmFtZXRlciBvZiBib3hwbG90LCB3aGljaCB3ZSBsZXQgZGVmYXVsdCB0byB0aGUgdmFsdWUgMS41IHVzZWQgYnkgUi4gVGhlIGhlaWdodCBvZiB0aGUgYm94IGlzIHRoZSBpbnRlcnF1YXJ0aWxlIHJhbmdlLCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSA3NXRoIGFuZCAyNXRoIHF1YW50aWxlcy4gSW4gdGhpcyBjYXNlIHRoYXQgZGlmZmVyZW5jZSBpcyAyLjguIFRoZSB3aGlza2VycyBhcmUgZHJhd24gdG8gYmUgYSBsZW5ndGggb2YgcmFuZ2UgeCAyLjggb3IgMS41KjIuOC4gVGhpcyBzaG93cyB1cyByb3VnaGx5IGhvdyBtYW55LCBpZiBhbnksIGRhdGEgcG9pbnRzIGFyZSBvdXRsaWVycywgdGhhdCBpcywgYmV5b25kIHRoaXMgcmFuZ2Ugb2YgdmFsdWVzLg0KDQpOb3RlIHRoYXQgYGJveHBsb3RgIGlzIHBhcnQgb2YgUidzIGJhc2UgcGxvdHRpbmcgcGFja2FnZS4gQSBuaWNlIGZlYXR1cmUgdGhhdCB0aGlzIHBhY2thZ2UgcHJvdmlkZXMgaXMgaXRzIGFiaWxpdHkgdG8gb3ZlcmxheSBmZWF0dXJlcy4gVGhhdCBpcywgeW91IGNhbiBhZGQgdG8gKGFubm90YXRlKSBhbiBleGlzdGluZyBwbG90Lg0KDQpUbyBzZWUgdGhpcywgcnVuIHRoZSBSIGNvbW1hbmQgYGFibGluZWAgd2l0aCB0aGUgYXJndW1lbnQgYGhgIGVxdWFsIHRvIGAxMmAuIFJlY2FsbCB0aGF0IDEyIGlzIHRoZSBFUEEgc3RhbmRhcmQgZm9yIGFpciBxdWFsaXR5Lg0KDQpgYGB7cn0NCmJveHBsb3QocHBtLCBjb2wgPSAiYmx1ZSIpDQphYmxpbmUoaD0xMikNCmBgYA0KDQpXaGF0IGRvIHlvdSB0aGluayB0aGlzIGNvbW1hbmQgZGlkPw0KDQoxLiAqKmRyZXcgYSBob3Jpem9udGFsIGxpbmUgYXQgMTIqKg0KMi4gaGlkIDEyIHJhbmRvbSBkYXRhIHBvaW50cw0KMy4gZHJldyBhIHZlcnRpY2FsIGxpbmUgYXQgMTINCjQuIG5vdGhpbmcNCg0KU28gYGFibGluZWAgImFkZHMgb25lIG9yIG1vcmUgc3RyYWlnaHQgbGluZXMgdGhyb3VnaCB0aGUgY3VycmVudCBwbG90LiIgV2Ugc2VlIGZyb20gdGhlIHBsb3QgdGhhdCB0aGUgYnVsayBvZiB0aGUgbWVhc3VyZWQgY291bnRpZXMgY29tcGx5IHdpdGggdGhlIHN0YW5kYXJkIHNpbmNlIHRoZXkgZmFsbCB1bmRlciB0aGUgbGluZSBtYXJraW5nIHRoYXQgc3RhbmRhcmQuDQoNCjwvYnI+DQoNCk5vdyB1c2UgdGhlIFIgY29tbWFuZCBgaGlzdGAgKGFub3RoZXIgZnVuY3Rpb24gZnJvbSB0aGUgYmFzZSBwYWNrYWdlKSB3aXRoIHRoZSBhcmd1bWVudCBgcHBtYC4gU3BlY2lmeSB0aGUgY29sb3IgcGFyYW1ldGVyIGBjb2xgIGVxdWFsIHRvIGAiZ3JlZW4iYC4gVGhpcyB3aWxsIHBsb3QgYSBoaXN0b2dyYW0gb2YgdGhlIGRhdGEuDQoNCmBgYHtyfQ0KaGlzdChwcG0sIGNvbCA9ICJncmVlbiIpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBnaXZlcyB1cyBhIGxpdHRsZSBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IG91ciBkYXRhLCBzcGVjaWZpY2FsbHkgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcG9sbHV0aW9uIGNvdW50cywgb3IgaG93IG1hbnkgY291bnRpZXMgZmFsbCBpbnRvIGVhY2ggYnVja2V0IG9mIG1lYXN1cmVtZW50cy4NCg0KV2hhdCBhcmUgdGhlIG1vc3QgZnJlcXVlbnQgcG9sbHV0aW9uIGNvdW50cz8NCg0KMS4gYmV0d2VlbiAxMiBhbmQgMTQNCjIuIHVuZGVyIDUNCjMuIGJldHdlZW4gNiBhbmQgOA0KNC4gKipiZXR3ZWVuIDkgYW5kIDEyKioNCg0KPC9icj4NCg0KTm93IHJ1biB0aGUgUiBjb21tYW5kIGBydWdgIHdpdGggdGhlIGFyZ3VtZW50IGBwcG1gLg0KDQpgYGB7cn0NCmhpc3QocHBtLCBjb2wgPSAiZ3JlZW4iKQ0KcnVnKHBwbSkNCmBgYA0KDQpUaGlzIG9uZS1kaW1lbnNpb25hbCBwbG90LCB3aXRoIGl0cyBncmF5c2NhbGUgcmVwcmVzZW50YXRpb24sIGdpdmVzIHlvdSBhIGxpdHRsZSBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IGhvdyBtYW55IGRhdGEgcG9pbnRzIGFyZSBpbiBlYWNoIGJ1Y2tldCBhbmQgd2hlcmUgdGhleSBsaWUgd2l0aGluIHRoZSBidWNrZXQuIEl0IHNob3dzICh0aHJvdWdoIGRlbnNpdHkgb2YgdGljayBtYXJrcykgdGhhdCB0aGUgZ3JlYXRlc3QgY29uY2VudHJhdGlvbiBvZiBjb3VudGllcyBoYXMgYmV0d2VlbiA5IGFuZCAxMiBtaWNyb2dyYW1zIHBlciBjdWJpYyBtZXRlciBqdXN0IGFzIHRoZSBoaXN0b2dyYW0gZGlkLg0KDQpUbyBpbGx1c3RyYXRlIHRoaXMgYSBsaXR0bGUgbW9yZSwgd2UndmUgZGVmaW5lZCBmb3IgeW91IHR3byB2ZWN0b3JzLCBgaGlnaGAgYW5kIGBsb3dgLCBjb250YWluaW5nIHBvbGx1dGlvbiBkYXRhIG9mIGhpZ2ggKGdyZWF0ZXIgdGhhbiAxNSkgYW5kIGxvdyAobGVzcyB0aGFuIDUpIHZhbHVlcyByZXNwZWN0aXZlbHkuIExvb2sgYXQgYGxvd2Agbm93IGFuZCBzZWUgaG93IGl0IHJlbGF0ZXMgdG8gdGhlIG91dHB1dCBvZiBydWcuDQoNCmBgYHtyfQ0KbG93DQpgYGANCg0KSXQgY29uZmlybXMgdGhhdCB0aGVyZSBhcmUgdHdvIGRhdGEgcG9pbnRzIGJldHdlZW4gMyBhbmQgNCBhbmQgbWFueSBiZXR3ZWVuIDQgYW5kIDUuIE5vdyBsb29rIGF0IGBoaWdoYC4NCg0KYGBge3J9DQpoaWdoDQpgYGANCg0KQWdhaW4sIHdlIHNlZSBvbmUgZGF0YSBwb2ludCBncmVhdGVyIHRoYW4gMTgsIG9uZSBiZXR3ZWVuIDE3IGFuZCAxOCwgc2V2ZXJhbCBiZXR3ZWVuIDE2IGFuZCAxNyBhbmQgdHdvIGJldHdlZW4gMTUgYW5kIDE2LCB2ZXJpZnlpbmcgd2hhdCBgcnVnYCBpbmRpY2F0ZWQuDQoNCk5vdyByZXJ1biBgaGlzdGAgd2l0aCAzIGFyZ3VtZW50cywgYHBwbWAgYXMgaXRzIGZpcnN0LCBgY29sYCBlcXVhbCB0byBgImdyZWVuImAsIGFuZCB0aGUgYXJndW1lbnQgYGJyZWFrc2AgZXF1YWwgdG8gYDEwMGAuDQoNCmBgYHtyfQ0KaGlzdChwcG0sIGNvbD0iZ3JlZW4iLCBicmVha3MgPSAxMDApDQpgYGANCg0KV2hhdCBkbyB5b3UgdGhpbmsgdGhlIGJyZWFrcyBhcmd1bWVudCBzcGVjaWZpZXMgaW4gdGhpcyBjYXNlPw0KDQoxLiB0aGUgbnVtYmVyIG9mIHN0YXJzIGluIHRoZSBza3kNCjIuICoqdGhlIG51bWJlciBvZiBidWNrZXRzIHRvIHNwbGl0IHRoZSBkYXRhIGludG8qKg0KMy4gdGhlIG51bWJlciBvZiBjb3VudGllcyBleGNlZWRpbmcgdGhlIEVQQSBzdGFuZGFyZA0KNC4gdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyB0byBncmFwaA0KDQpTbyB0aGlzIGhpc3RvZ3JhbSB3aXRoIG1vcmUgYnVja2V0cyBpcyBub3QgbmVhcmx5IGFzIHNtb290aCBhcyB0aGUgcHJlY2VkaW5nIG9uZS4gSW4gZmFjdCwgaXQncyBhIGxpdHRsZSB0b28gbm9pc3kgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gY2xlYXJseS4gV2hlbiB5b3UncmUgcGxvdHRpbmcgaGlzdG9ncmFtcyB5b3UgbWlnaHQgaGF2ZSB0byBleHBlcmltZW50IHdpdGggdGhlIGFyZ3VtZW50IGBicmVha3NgIHRvIGdldCBhIGdvb2QgaWRlYSBvZiB5b3VyIGRhdGEncyBkaXN0cmlidXRpb24uIEZvciBmdW4gbm93LCByZXJ1biB0aGUgUiBjb21tYW5kIGBydWdgIHdpdGggdGhlIGFyZ3VtZW50IGBwcG1gLg0KDQpgYGB7cn0NCmhpc3QocHBtLCBjb2w9ImdyZWVuIiwgYnJlYWtzID0gMTAwKQ0KcnVnKHBwbSkNCmBgYA0KDQpTZWUgaG93IGBydWdgIHdvcmtzIHdpdGggdGhlIGV4aXN0aW5nIHBsb3Q/IEl0IGF1dG9tYXRpY2FsbHkgYWRqdXN0ZWQgaXRzIHBvY2tldCBzaXplIHRvIHRoYXQgb2YgdGhlIGxhc3QgcGxvdCBwbG90dGVkLg0KDQpOb3cgcmVydW4gYGhpc3RgIHdpdGggYHBwbWAgYXMgdGhlIGRhdGEgYW5kIGBjb2xgIGVxdWFsIHRvIGAiZ3JlZW4iYC4NCg0KYGBge3J9DQpoaXN0KHBwbSwgY29sID0gImdyZWVuIikNCmBgYA0KDQpOb3cgcnVuIHRoZSBjb21tYW5kIGBhYmxpbmVgIHdpdGggdGhlIGFyZ3VtZW50IGB2YCBlcXVhbCB0byAxMiBhbmQgdGhlIGFyZ3VtZW50IGBsd2RgIGVxdWFsIHRvIDIuDQoNCmBgYHtyfQ0KaGlzdChwcG0sIGNvbCA9ICJncmVlbiIpDQphYmxpbmUodj0xMiwgbHdkPTIpDQpgYGANCg0KU2VlIHRoZSB2ZXJ0aWNhbCBsaW5lIGF0IDEyPyBOb3QgdmVyeSB2aXNpYmxlLCBpcyBpdCwgZXZlbiB0aG91Z2ggeW91IHNwZWNpZmllZCBhIGxpbmUgd2lkdGggb2YgMj8gUnVuIGBhYmxpbmVgIHdpdGggdGhlIGFyZ3VtZW50IGB2YCBlcXVhbCB0byBgbWVkaWFuKHBwbSlgLCB0aGUgYXJndW1lbnQgYGNvbGAgZXF1YWwgdG8gYCJtYWdlbnRhImAsIGFuZCB0aGUgYXJndW1lbnQgYGx3ZGAgZXF1YWwgdG8gYDRgLg0KDQpgYGB7cn0NCmhpc3QocHBtLCBjb2wgPSAiZ3JlZW4iKQ0KYWJsaW5lKHY9bWVkaWFuKHBwbSksIGNvbD0ibWFnZW50YSIsIGx3ZD00KQ0KYGBgDQoNCkJldHRlciwgcmlnaHQ/IFRoaWNrZXIgYW5kIG1vcmUgb2YgYSBjb250cmFzdCBpbiBjb2xvci4gVGhpcyBzaG93cyB0aGF0IGFsdGhvdWdoIHRoZSBtZWRpYW4gKDUwJSkgaXMgYmVsb3cgdGhlIHN0YW5kYXJkLCB0aGVyZSBhcmUgYSBmYWlyIG51bWJlciBvZiBjb3VudGllcyBpbiB0aGUgVS5TIHRoYXQgaGF2ZSBwb2xsdXRpb24gbGV2ZWxzIGhpZ2hlciB0aGFuIHRoZSBzdGFuZGFyZC4NCg0KTm93IHJlY2FsbCB0aGF0IG91ciBwb2xsdXRpb24gZGF0YSBoYWQgNSBjb2x1bW5zIG9mIGluZm9ybWF0aW9uLiBTbyBmYXIgd2UndmUgb25seSBsb29rZWQgYXQgdGhlIGBwbTI1YCBjb2x1bW4uIFdlIGNhbiBhbHNvIGxvb2sgYXQgb3RoZXIgaW5mb3JtYXRpb24uIFRvIHJlbWluZCB5b3Vyc2VsZiB3aGF0J3MgdGhlcmUgcnVuIHRoZSBSIGNvbW1hbmQgYG5hbWVzYCB3aXRoIGBwb2xsdXRpb25gIGFzIHRoZSBhcmd1bWVudC4NCg0KYGBge3J9DQpuYW1lcyhwb2xsdXRpb24pDQpgYGANCg0KYExvbmdpdHVkZWAgYW5kIGBsYXRpdHVkZWAgZG9uJ3Qgc291bmQgaW50ZXJlc3RpbmcsIGFuZCBlYWNoIGZpcHMgaXMgdW5pcXVlIHNpbmNlIGl0IGlkZW50aWZpZXMgc3RhdGVzIChmaXJzdCAyIGRpZ2l0cykgYW5kIGNvdW50aWVzIChsYXN0IDMgZGlnaXRzKS4gTGV0J3MgbG9vayBhdCB0aGUgYHJlZ2lvbmAgY29sdW1uIHRvIHNlZSB3aGF0J3MgdGhlcmUuIFJ1biB0aGUgUiBjb21tYW5kIGB0YWJsZWAgb24gdGhpcyBjb2x1bW4uIFVzZSB0aGUgY29uc3RydWN0IGBwb2xsdXRpb24kcmVnaW9uYC4gU3RvcmUgdGhlIHJlc3VsdCBpbiB0aGUgdmFyaWFibGUgYHJlZ2AuDQoNCmBgYHtyfQ0KcmVnIDwtIHRhYmxlKHBvbGx1dGlvbiRyZWdpb24pDQpgYGANCg0KTG9vayBhdCBgcmVnYCBub3cuDQoNCmBgYHtyfQ0KcmVnDQpgYGANCg0KTG90IG1vcmUgY291bnRpZXMgaW4gdGhlIGVhc3QgdGhhbiB3ZXN0LiBXZSdsbCB1c2UgdGhlIFIgY29tbWFuZCBgYmFycGxvdGAgKGFub3RoZXIgdHlwZSBvZiBvbmUtZGltZW5zaW9uYWwgc3VtbWFyeSkgdG8gcGxvdCB0aGlzIGluZm9ybWF0aW9uLiBDYWxsIGBiYXJwbG90YCB3aXRoIGByZWdgIGFzIGl0cyBmaXJzdCBhcmd1bWVudCwgdGhlIGFyZ3VtZW50IGBjb2xgIGVxdWFsIHRvIGAid2hlYXQiYCwgYW5kIHRoZSBhcmd1bWVudCBgbWFpbmAgZXF1YWwgdG8gdGhlIHN0cmluZyBgIk51bWJlciBvZiBDb3VudGllcyBpbiBFYWNoIFJlZ2lvbiJgLg0KDQpgYGB7cn0NCmJhcnBsb3QocmVnLCBjb2w9IndoZWF0IiwgbWFpbj0iTnVtYmVyIG9mIENvdW50aWVzIGluIEVhY2ggUmVnaW9uIikNCmBgYA0KDQpXaGF0IGRvIHlvdSB0aGluayB0aGUgYXJndW1lbnQgYG1haW5gIHNwZWNpZmllcz8NCg0KMS4gKip0aGUgdGl0bGUgb2YgdGhlIGdyYXBoKioNCjIuIHRoZSB4IGF4aXMgbGFiZWwNCjMuIEkgY2FuJ3QgdGVsbA0KNC4gdGhlIHkgYXhpcyBsYWJlbA0KDQo8L2JyPg0KDQpTbyB3ZSd2ZSBzZWVuIHNldmVyYWwgZXhhbXBsZXMgb2Ygb25lLWRpbWVuc2lvbmFsIGdyYXBocyB0aGF0IHN1bW1hcml6ZSBkYXRhLiBUd28gZGltZW5zaW9uYWwgZ3JhcGhzIGluY2x1ZGUgc2NhdHRlcnBsb3RzLCBtdWx0aXBsZSBncmFwaHMgd2hpY2ggd2UnbGwgc2VlIG1vcmUgZXhhbXBsZXMgb2YsIGFuZCBvdmVybGF5ZWQgb25lLWRpbWVuc2lvbmFsIHBsb3RzIHdoaWNoIHRoZSBSIHBhY2thZ2VzIHN1Y2ggYXMgKmxhdHRpY2UqIGFuZCAqZ2dwbG90MiogcHJvdmlkZS4NCg0KU29tZSBncmFwaHMgaGF2ZSBtb3JlIHRoYW4gdHdvLWRpbWVuc2lvbnMuIFRoZXNlIGluY2x1ZGUgb3ZlcmxheWVkIG9yIG11bHRpcGxlIHR3by1kaW1lbnNpb25hbCBwbG90cyBhbmQgc3Bpbm5pbmcgcGxvdHMuIFNvbWUgdGhyZWUtZGltZW5zaW9uYWwgcGxvdHMgYXJlIHRyaWNreSB0byB1bmRlcnN0YW5kIHNvIGhhdmUgbGltaXRlZCBhcHBsaWNhdGlvbnMuIFdlJ2xsIHNlZSBzb21lIGV4YW1wbGVzIG5vdyBvZiBtb3JlIGNvbXBsaWNhdGVkIGdyYXBocywgaW4gcGFydGljdWxhciwgd2UnbGwgc2hvdyB0d28gZ3JhcGhzIHRvZ2V0aGVyLg0KDQpGaXJzdCB3ZSdsbCBzaG93IGhvdyBSLCBpbiBvbmUgbGluZSBhbmQgdXNpbmcgYmFzZSBwbG90dGluZywgY2FuIGRpc3BsYXkgbXVsdGlwbGUgYm94cGxvdHMuIFdlIHNpbXBseSBzcGVjaWZ5IHRoYXQgd2Ugd2FudCB0byBzZWUgdGhlIHBvbGx1dGlvbiBkYXRhIGFzIGEgZnVuY3Rpb24gb2YgcmVnaW9uLiBXZSBrbm93IHRoYXQgb3VyIHBvbGx1dGlvbiBkYXRhIGNoYXJhY3Rlcml6ZWQgZWFjaCBvZiB0aGUgNTc2IGVudHJpZXMgYXMgYmVsb25naW5nIHRvIG9uZSBvZiB0d28gcmVnaW9ucyAoZWFzdCBhbmQgd2VzdCkuDQoNCldlIHVzZSB0aGUgUiBmb3JtdWxhIHkgfiB4IHRvIHNob3cgdGhhdCB5IChpbiB0aGlzIGNhc2UgYHBtMjVgKSBkZXBlbmRzIG9uIHggKGByZWdpb25gKS4gU2luY2UgYm90aCBjb21lIGZyb20gdGhlIHNhbWUgZGF0YSBmcmFtZSAoYHBvbGx1dGlvbmApIHdlIGNhbiBzcGVjaWZ5IGEgZGF0YSBhcmd1bWVudCBgc2V0YCBlcXVhbCB0byBgcG9sbHV0aW9uYC4gQnkgZG9pbmcgdGhpcywgd2UgZG9uJ3QgaGF2ZSB0byB0eXBlIGBwb2xsdXRpb24kcG0yNWAgKG9yIGBwcG1gKSBhbmQgYHBvbGx1dGlvbiRyZWdpb25gLiBXZSBjYW4ganVzdCBzcGVjaWZ5IHRoZSBmb3JtdWxhIGBwbTI1fnJlZ2lvbmAuIENhbGwgYGJveHBsb3RgIG5vdyB3aXRoIHRoaXMgZm9ybXVsYSBhcyBpdHMgYXJndW1lbnQsIGRhdGEgZXF1YWwgdG8gYHBvbGx1dGlvbmAsIGFuZCBgY29sYCBlcXVhbCB0byBgInJlZCJgLg0KDQpgYGB7cn0NCmJveHBsb3QocG0yNX5yZWdpb24sIHBvbGx1dGlvbiwgY29sPSJyZWQiKQ0KYGBgDQoNClR3byBmb3IgdGhlIHByaWNlIG9mIG9uZSENCg0KU2ltaWxhcmx5IHdlIGNhbiBwbG90IG11bHRpcGxlIGhpc3RvZ3JhbXMgaW4gb25lIHBsb3QsIHRob3VnaCB0byBkbyB0aGlzIHdlIGhhdmUgdG8gdXNlIG1vcmUgdGhhbiBvbmUgUiBjb21tYW5kLiBGaXJzdCB3ZSBoYXZlIHRvIHNldCB1cCB0aGUgcGxvdCB3aW5kb3cgd2l0aCB0aGUgUiBjb21tYW5kIGBwYXJgIHdoaWNoIHNwZWNpZmllcyBob3cgd2Ugd2FudCB0byBsYXkgb3V0IHRoZSBwbG90cywgc2F5IG9uZSBhYm92ZSB0aGUgb3RoZXIuIFdlIGFsc28gdXNlIGBwYXJgIHRvIHNwZWNpZnkgbWFyZ2lucywgYSA0LWxvbmcgdmVjdG9yIHdoaWNoIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIGxpbmVzIGZvciB0aGUgYm90dG9tLCBsZWZ0LCB0b3AgYW5kIHJpZ2h0Lg0KDQpUeXBlIHRoZSBSIGNvbW1hbmQgcGFyKG1mcm93PWMoMiwxKSxtYXI9Yyg0LDQsMiwxKSkgbm93LiBEb24ndCBleHBlY3QgdG8gc2VlIGFueSBuZXcgcmVzdWx0Lg0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMSksbWFyPWMoNCw0LDIsMSkpDQpgYGANCg0KU28gd2Ugc2V0IHVwIHRoZSBwbG90IHdpbmRvdyBmb3IgdHdvIHJvd3MgYW5kIG9uZSBjb2x1bW4gd2l0aCB0aGUgYG1mcm93YCBhcmd1bWVudC4gVGhlIGBtYXJgIGFyZ3VtZW50IHNldCB1cCB0aGUgbWFyZ2lucy4NCg0KQmVmb3JlIHdlIHBsb3QgdGhlIGhpc3RvZ3JhbXMgbGV0J3MgZXhwbG9yZSB0aGUgUiBjb21tYW5kIGBzdWJzZXRgIHdoaWNoLCBub3Qgc3VycHJpc2luZ2x5LCAqInJldHVybnMgc3Vic2V0cyBvZiB2ZWN0b3JzLCBtYXRyaWNlcyBvciBkYXRhIGZyYW1lcyB3aGljaCBtZWV0IGNvbmRpdGlvbnMiKi4gV2UnbGwgdXNlIGBzdWJzZXRgIHRvIHB1bGwgb2ZmIHRoZSBkYXRhIHdlIHdhbnQgdG8gcGxvdC4NCg0KQ2FsbCBgc3Vic2V0YCBub3cgd2l0aCBgcG9sbHV0aW9uYCBhcyBpdHMgZmlyc3QgYXJndW1lbnQgYW5kIGEgYm9vbGVhbiBleHByZXNzaW9uIHRlc3RpbmcgYHJlZ2lvbmAgZm9yIGVxdWFsaXR5IHdpdGggdGhlIHN0cmluZyBgImVhc3QiYC4gUHV0IHRoZSByZXN1bHQgaW4gdGhlIHZhcmlhYmxlIGBlYXN0YC4NCg0KYGBge3J9DQplYXN0IDwtIHN1YnNldChwb2xsdXRpb24sIHJlZ2lvbiA9PSAiZWFzdCIpDQpgYGANCg0KVXNlIGBoZWFkYCB0byBsb29rIGF0IHRoZSBmaXJzdCBmZXcgZW50cmllcyBvZiBlYXN0Lg0KDQpgYGB7cn0NCmhlYWQoZWFzdCkNCmBgYA0KDQpTbyBgZWFzdGAgaG9sZHMgbW9yZSBpbmZvcm1hdGlvbiB0aGFuIHdlIG5lZWQuIFdlIGp1c3Qgd2FudCB0byBwbG90IGEgaGlzdG9ncmFtIHdpdGggdGhlIGBwbTI1YCBwb3J0aW9uLiBDYWxsIGBoaXN0YCBub3cgd2l0aCB0aGUgYHBtMjVgIHBvcnRpb24gb2YgYGVhc3RgIGFzIGl0cyBmaXJzdCBhcmd1bWVudCBhbmQgYGNvbGAgZXF1YWwgdG8gYCJncmVlbiJgIGFzIGl0cyBzZWNvbmQuDQoNCmBgYHtyfQ0KaGlzdChlYXN0JHBtMjUsIGNvbCA9ICJncmVlbiIpDQpgYGANCg0KU2VlPyBUaGUgY29tbWFuZCBgcGFyYCB0b2xkIFIgd2Ugd2VyZSBnb2luZyB0byBoYXZlIG9uZSBjb2x1bW4gd2l0aCAyIHJvd3MsIHNvIGl0IHBsYWNlZCB0aGlzIGhpc3RvZ3JhbSBpbiB0aGUgdG9wIHBvc2l0aW9uLg0KDQpOb3csIGhlcmUncyBhIGNoYWxsZW5nZSBmb3IgeW91LiBQbG90IHRoZSBoaXN0b2dyYW0gb2YgdGhlIGNvdW50aWVzIGZyb20gdGhlIHdlc3QgdXNpbmcganVzdCBvbmUgUiBjb21tYW5kLiBMZXQgdGhlIGFwcHJvcHJpYXRlIGBzdWJzZXRgIGNvbW1hbmQgKHdpdGggdGhlIGBwbTI1YCBwb3J0aW9uIHNwZWNpZmllZCkgYmUgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBgY29sYCAoZXF1YWwgdG8gYCJncmVlbiJgKSB0aGUgc2Vjb25kLiAgVG8gY3V0IGRvd24gb24geW91ciB0eXBpbmcsIHVzZSB0aGUgdXAgYXJyb3cga2V5IHRvIGdldCB5b3VyIGxhc3QgY29tbWFuZCBhbmQgcmVwbGFjZSBgImVhc3QiYCB3aXRoIHRoZSBzdWJzZXQgY29tbWFuZC4gTWFrZSBzdXJlIHRoZSBib29sZWFuIGFyZ3VtZW50IGNoZWNrcyBmb3IgZXF1YWxpdHkgYmV0d2VlbiBgcmVnaW9uYCBhbmQgYCJ3ZXN0ImAuDQoNCmBgYHtyfQ0KaGlzdChzdWJzZXQocG9sbHV0aW9uLCByZWdpb24gPT0gIndlc3QiKSRwbTI1LCBjb2wgPSAiZ3JlZW4iKQ0KYGBgDQoNClNlZSBob3cgUiBkb2VzIGFsbCB0aGUgbGFiZWxpbmcgZm9yIHlvdT8gTm90aWNlIHRoYXQgdGhlIHRpdGxlcyBhcmUgZGlmZmVyZW50IHNpbmNlIHdlIHVzZWQgZGlmZmVyZW50IGNvbW1hbmRzIGZvciB0aGUgdHdvIHBsb3RzLiBMZXQncyBsb29rIGF0IHNvbWUgc2NhdHRlciBwbG90cyBub3cuDQoNCjwvYnI+DQoNCioqU2NhdHRlciBwbG90cyoqIGFyZSB0d28tZGltZW5zaW9uYWwgcGxvdHMgd2hpY2ggc2hvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHZhcmlhYmxlcywgdXN1YWxseSB4IGFuZCB5LiBMZXQncyBsb29rIGF0IGEgc2NhdHRlcnBsb3Qgc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYGxhdGl0dWRlYCBhbmQgdGhlIGBwbTI1YCBkYXRhLiBXZSdsbCB1c2UgcGxvdCwgYSBmdW5jdGlvbiBmcm9tIFIncyBiYXNlIHBsb3R0aW5nIHBhY2thZ2UuDQoNCldlJ3ZlIHNlZW4gdGhhdCB3ZSBjYW4gdXNlIGEgZnVuY3Rpb24gY2FsbCBhcyBhbiBhcmd1bWVudCB3aGVuIGNhbGxpbmcgYW5vdGhlciBmdW5jdGlvbi4gV2UnbGwgZG8gdGhpcyBhZ2FpbiB3aGVuIHdlIGNhbGwgcGxvdCB3aXRoIHRoZSBhcmd1bWVudHMgYGxhdGl0dWRlYCBhbmQgYHBtMjVgIHdoaWNoIGFyZSBib3RoIGZyb20gb3VyIGRhdGEgZnJhbWUgYHBvbGx1dGlvbmAuIFdlJ2xsIGNhbGwgcGxvdCBmcm9tIGluc2lkZSB0aGUgUiBjb21tYW5kIGB3aXRoYCB3aGljaCBldmFsdWF0ZXMgKiJhbiBSIGV4cHJlc3Npb24gaW4gYW4gZW52aXJvbm1lbnQgY29uc3RydWN0ZWQgZnJvbSBkYXRhIiouIFdlJ2xsIHVzZSBgcG9sbHV0aW9uYCBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gYHdpdGhgIGFuZCB0aGUgY2FsbCB0byBgcGxvdGAgYXMgdGhlIHNlY29uZC4gVGhpcyBhbGxvd3MgdXMgdG8gYXZvaWQgdHlwaW5nIGAicG9sbHV0aW9uJCJgIGJlZm9yZSB0aGUgYXJndW1lbnRzIHRvIGBwbG90YCwgc28gaXQgc2F2ZXMgdXMgc29tZSB0eXBpbmcgYW5kIGFkZHMgdG8geW91ciBiYXNlIG9mIFIga25vd2xlZGdlLiBUcnkgdGhpcyBub3cuDQoNCmBgYHtyfQ0Kd2l0aChwb2xsdXRpb24sIHBsb3QobGF0aXR1ZGUsIHBtMjUpKQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGUgZmlyc3QgYXJndW1lbnQgaXMgcGxvdHRlZCBhbG9uZyB0aGUgeC1heGlzIGFuZCB0aGUgc2Vjb25kIGFsb25nIHRoZSB5LiBOb3cgdXNlIGBhYmxpbmVgIHRvIGFkZCBhIGhvcml6b250YWwgbGluZSBhdCAxMi4gVXNlIHR3byBhZGRpdGlvbmFsIGFyZ3VtZW50cywgYGx3ZGAgZXF1YWwgdG8gMiBhbmQgYGx0eWAgYWxzbyBlcXVhbCB0byAyLiBTZWUgd2hhdCBoYXBwZW5zLg0KDQpgYGB7cn0NCndpdGgocG9sbHV0aW9uLCBwbG90KGxhdGl0dWRlLCBwbTI1KSkNCmFibGluZShoPTEyLCBsd2Q9MiwgbHR5PTIpDQpgYGANCg0KU2VlIGhvdyBgbHR5PTJgIG1hZGUgdGhlIGxpbmUgZGFzaGVkPyBOb3cgbGV0J3MgcmVwbG90IHRoZSBzY2F0dGVycGxvdC4gVGhpcyB0aW1lLCBpbnN0ZWFkIG9mIHVzaW5nIGB3aXRoYCwgY2FsbCBgcGxvdGAgZGlyZWN0bHkgd2l0aCAzIGFyZ3VtZW50cy4gVGhlIGZpcnN0IDIgYXJlIGBwb2xsdXRpb24kbGF0aXR1ZGVgIGFuZCBgcHBtYC4gVGhlIHRoaXJkIGFyZ3VtZW50LCBgY29sYCwgd2UnbGwgdXNlIHRvIGFkZCBjb2xvciBhbmQgbW9yZSBpbmZvcm1hdGlvbiB0byBvdXIgcGxvdC4gU2V0IHRoaXMgYXJndW1lbnQgKGBjb2xgKSBlcXVhbCB0byBgcG9sbHV0aW9uJHJlZ2lvbmAgYW5kIHNlZSB3aGF0IGhhcHBlbnMuDQoNCmBgYHtyfQ0KcGxvdChwb2xsdXRpb24kbGF0aXR1ZGUsIHBwbSwgY29sPXBvbGx1dGlvbiRyZWdpb24pDQpgYGANCg0KV2UndmUgZ290IHR3byBjb2xvcnMgb24gdGhlIG1hcCB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGNvdW50aWVzIGluIHRoZSBlYXN0IGFuZCB0aG9zZSBpbiB0aGUgd2VzdC4gQ2FuIHdlIGZpZ3VyZSBvdXQgd2hpY2ggY29sb3IgaXMgZWFzdCBhbmQgd2hpY2ggd2VzdD8gU2VlIHRoYXQgdGhlIGhpZ2ggKGdyZWF0ZXIgdGhhbiA1MCkgYW5kIGxvdyAobGVzcyB0aGFuIDI1KSBsYXRpdHVkZXMgYXJlIGJvdGggcmVkLiBMYXRpdHVkZXMgaW5kaWNhdGUgZGlzdGFuY2UgZnJvbSB0aGUgZXF1YXRvciwgc28gd2hpY2ggaGFsZiBvZiB0aGUgVS5TLiAoZWFzdCBvciB3ZXN0KSBoYXMgY291bnRpZXMgYXQgdGhlIGV4dHJlbWUgbm9ydGggYW5kIHNvdXRoPw0KDQoxOiAqKndlc3QqKg0KMjogZWFzdA0KDQpBcyBiZWZvcmUsIHVzZSBgYWJsaW5lYCB0byBhZGQgYSBob3Jpem9udGFsIGxpbmUgYXQgMTIuIFVzZSB0d28gYWRkaXRpb25hbCBhcmd1bWVudHMsIGBsd2RgIGVxdWFsIHRvIDIgYW5kIGBsdHlgIGFsc28gZXF1YWwgdG8gMi4NCg0KYGBge3J9DQpwbG90KHBvbGx1dGlvbiRsYXRpdHVkZSwgcHBtLCBjb2w9cG9sbHV0aW9uJHJlZ2lvbikNCmFibGluZShoPTEyLCBsd2Q9MiwgbHR5PTIpDQpgYGANCg0KV2Ugc2VlIG1hbnkgY291bnRpZXMgYXJlIGFib3ZlIHRoZSBoZWFsdGh5IHN0YW5kYXJkIHNldCBieSB0aGUgRVBBLCBidXQgaXQncyBoYXJkIHRvIHRlbGwgb3ZlcmFsbCwgd2hpY2ggcmVnaW9uLCBlYXN0IG9yIHdlc3QsIGlzIHdvcnNlLg0KDQpMZXQncyBwbG90IHR3byBzY2F0dGVycGxvdHMgZGlzdGluZ3Vpc2hlZCBieSByZWdpb24uDQoNCkFzIHdlIGRpZCB3aXRoIG11bHRpcGxlIGhpc3RvZ3JhbXMsIHdlIGZpcnN0IGhhdmUgdG8gc2V0IHVwIHRoZSBwbG90IHdpbmRvdyB3aXRoIHRoZSBSIGNvbW1hbmQgYHBhcmAuIFRoaXMgdGltZSwgbGV0J3MgcGxvdCB0aGUgc2NhdHRlcnBsb3RzIHNpZGUgYnkgc2lkZSAob25lIHJvdyBhbmQgdHdvIGNvbHVtbnMpLiBXZSBhbHNvIG5lZWQgdG8gdXNlIGRpZmZlcmVudCBtYXJnaW5zLiBUeXBlIHRoZSBSIGNvbW1hbmQgYHBhcihtZnJvdyA9IGMoMSwgMiksIG1hciA9IGMoNSwgNCwgMiwgMSkpYCBub3cuIERvbid0IGV4cGVjdCB0byBzZWUgYW55IG5ldyByZXN1bHQuDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygxLCAyKSwgbWFyID0gYyg1LCA0LCAyLCAxKSkNCmBgYA0KDQpGb3IgdGhlIGZpcnN0IHNjYXR0ZXJwbG90LCBvbiB0aGUgbGVmdCwgd2UnbGwgcGxvdCB0aGUgYGxhdGl0dWRlc2AgYW5kIGBwbTI1YCBjb3VudHMgZnJvbSB0aGUgd2VzdC4gV2UgYWxyZWFkeSBwdWxsZWQgb3V0IHRoZSBpbmZvcm1hdGlvbiBmb3IgdGhlIGNvdW50aWVzIGluIHRoZSBlYXN0LiBMZXQncyBub3cgZ2V0IHRoZSBpbmZvcm1hdGlvbiBmb3IgdGhlIGNvdW50aWVzIGZyb20gdGhlIHdlc3QuIENyZWF0ZSB0aGUgdmFyaWFibGUgYHdlc3RgIGJ5IHVzaW5nIHRoZSBgc3Vic2V0YCBjb21tYW5kIHdpdGggYHBvbGx1dGlvbmAgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCB0aGUgYXBwcm9wcmlhdGUgYm9vbGVhbiBhcyB0aGUgc2Vjb25kLg0KDQpgYGB7cn0NCndlc3QgPC0gc3Vic2V0KHBvbGx1dGlvbiwgcmVnaW9uID09ICJ3ZXN0IikNCmBgYA0KDQpOb3cgY2FsbCBgcGxvdGAgd2l0aCB0aHJlZSBhcmd1bWVudHMuIFRoZXNlIGFyZSBgd2VzdCRsYXRpdHVkZWAgKHgtYXhpcyksIGB3ZXN0JHBtMjVgICh5LWF4aXMpLCBhbmQgdGhlIGFyZ3VtZW50IGBtYWluYCBlcXVhbCB0byB0aGUgc3RyaW5nIGAiV2VzdCJgICh0aXRsZSkuIERvIHRoaXMgbm93Lg0KDQpgYGB7cn0NCnBsb3Qod2VzdCRsYXRpdHVkZSwgd2VzdCRwbTI1LCBtYWluID0gIldlc3QiKQ0KYGBgDQoNCkZvciB0aGUgc2Vjb25kIHNjYXR0ZXJwbG90LCBvbiB0aGUgcmlnaHQsIHdlJ2xsIHBsb3QgdGhlIGBsYXRpdHVkZXNgIGFuZCBgcG0yNWAgY291bnRzIGZyb20gdGhlIGVhc3QuDQoNCmBgYHtyfQ0KcGxvdChlYXN0JGxhdGl0dWRlLCBlYXN0JHBtMjUsIG1haW4gPSAiRWFzdCIpDQpgYGANCg0KU2VlIGhvdyBSIHRvb2sgY2FyZSBvZiBhbGwgdGhlIGRldGFpbHMgZm9yIHlvdT8gTmljZSwgcmlnaHQ/IEl0IGxvb2tzIGxpa2UgdGhlcmUgYXJlIG1vcmUgZGlydHkgY291bnRpZXMgaW4gdGhlIGVhc3QgYnV0IHRoZSBleHRyZW1lIGRpcnQgKGdyZWF0ZXIgdGhhbiAxNSkgaXMgaW4gdGhlIHdlc3QuDQoNCjwvYnI+DQoNCkxldCdzIHN1bW1hcml6ZSBhbmQgcmV2aWV3Lg0KDQpXaGljaCBvZiB0aGUgZm9sbG93aW5nIGNoYXJhY3Rlcml6ZXMgZXhwbG9yYXRvcnkgcGxvdHM/DQoNCjEuIHNsb3cgYW5kIHN0ZWFkeQ0KMi4gcXVpY2sgYW5kIGRlYWQNCjMuIHNsb3cgYW5kIGNsZWFuDQo0LiAqKnF1aWNrIGFuZCBkaXJ0eSoqDQoNCg0KVHJ1ZSBvciBmYWxzZT8gUGxvdHMgbGV0IHlvdSBzdW1tYXJpemUgdGhlIGRhdGEgKHVzdWFsbHkgZ3JhcGhpY2FsbHkpIGFuZCBoaWdobGlnaHQgYW55IGJyb2FkIGZlYXR1cmVzDQoNCjEuICoqVHJ1ZSoqDQoyLiBGYWxzZQ0KDQoNCldoaWNoIG9mIHRoZSBmb2xsb3dpbmcgZG8gcGxvdHMgTk9UIGRvPw0KDQoxLiBTdWdnZXN0IG1vZGVsaW5nIHN0cmF0ZWdpZXMgZm9yIHRoZSAibmV4dCBzdGVwIg0KMi4gU3VtbWFyaXplIHRoZSBkYXRhICh1c3VhbGx5IGdyYXBoaWNhbGx5KSBhbmQgaGlnaGxpZ2h0IGFueSBicm9hZCBmZWF0dXJlcw0KMy4gKipDb25jbHVkZSB0aGF0IHlvdSBhcmUgQUxXQVlTIHJpZ2h0KioNCjQuIEV4cGxvcmUgYmFzaWMgcXVlc3Rpb25zIGFuZCBoeXBvdGhlc2VzIChhbmQgcGVyaGFwcyBydWxlIHRoZW0gb3V0KQ0KDQoNCkNvbmdyYXRzISBZb3UndmUgY29uY2x1ZGVkIGV4cGxvcmluZyB0aGlzIGxlc3NvbiBvbiBncmFwaGljcy4gV2UgaG9wZSB5b3UgZGlkbid0IGZpbmQgaXQgdG9vIHF1aWNrIG9yIGRpcnR5Lg0KDQo8L2JyPg0KDQotLS0NCg0KPC9icj4NCg0KPGNlbnRlcj5FTkQ8L2NlbnRlcj4NCg0KPC9icj4NCg0KLS0t