Image source: thenewstack
Image source: thenewstack

Why visualize?

Data visualization is the graphical representation of information and data. By using visual elements like charts, graphs, and maps, data visualization tools provide an accessible way to see and understand trends, outliers, and patterns in data. Hidden within your data lie important insights. But the challenge is that you can’t always connect the dots by looking at raw numbers alone. When you look at your data presented in a visual format, patterns, connections, and other “a-ha” insights emerge that would otherwise remain out of sight.

The earliest form of data visualization can be traced back to the Egyptians in the pre-17th century, largely used to assist in navigation. As time progressed, people leveraged data visualizations for broader applications, such as in economic, social, health, and environmental disciplines.

There are many different ways to visualize data. Some of the most common techniques are:

Important: By convention, the x-axis is the independent variable and the dependent variable is plotted on the y-axis.

So far we have been using base R visualization tools (e.g. when you used the command: plot). However, there are various other packages that we can use to produce nicer and more sophisticated graphs. One of the most popular of such packages is called ggplot2.

But what are R packages?

One of the primary reasons for R’s popularity is its extensive package ecosystem. On R’s main package repository: Comprehensive R Archive Network (CRAN) alone you have over 10,000 packages available to choose from. You can see the list of available packages on CRAN here. Yet, when you first install R you only get a very limited set of core packages “out of the box”. Any further packages that you’d like to use you have to install yourself.

Installing packages

To be able to use a (non-base) package for the first time, we will need to first install it on our system. Installing packages from CRAN couldn’t be easier! Simply type install.packages() with the name of your desired package in quotes as first argument.

install.packages("ggplot2")
Installing package into ‘/home/kazanjian/R/x86_64-pc-linux-gnu-library/4.3’
(as ‘lib’ is unspecified)
trying URL 'https://cloud.r-project.org/src/contrib/ggplot2_3.4.4.tar.gz'
Content type 'application/x-gzip' length 3159578 bytes (3.0 MB)
==================================================
downloaded 3.0 MB

* installing *source* package ‘ggplot2’ ...
** package ‘ggplot2’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** data
*** moving datasets to lazyload DB
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (ggplot2)

The downloaded source packages are in
    ‘/tmp/Rtmpxwm0IQ/downloaded_packages’

Note: Although I have included and executed the code within my notebook here (to show you how it is done), it is usually advisable to not include this in R notebooks but directly in your console (the box at the lower left corner usually in RStudio) as

  1. You will only need to install it once on each computer you use.
  2. It will often produce very long codes that will make your html (or pdf) outputs very messy.

Once a package is installed on your system, you can load it to R by using the command: library(package name). You will have to do this every time after you restart R. Thus, unlike the install.packages command, it is recommended to include this at the top of each of your notebooks, to make sure the packages you need are loaded.

library(ggplot2)

ggplot2

ggplot2 is a powerful data visualization package in the R programming language. It is based on the grammar of graphics, which is a way of describing and building graphs using a structured approach. ggplot2 allows you to create a wide variety of plots and graphics, including scatter plots, bar plots, line plots, histograms, and more, with a high degree of customization and flexibility. It is now over 10 years old and is widely used in the data science and statistical communities for creating visually appealing and informative plots from data.

While it is slightly more complex to execute than the simple plots in base R, once you understand its syntax (by practicing!) you will get the hang of it quickly. It is best to imagine how ggplot2 works as making graphs with different layers. In most cases you start with ggplot(), supply a dataset and aesthetic mapping (with aes()) This will be the background and axes of your graph. You then add on layers (like geom_point() or geom_histogram()) These will be the points, lines or bars on top of your background, scales (like scale_colour_brewer()), faceting specifications (like facet_wrap()) and coordinate systems (like coord_flip()). We will cover some of these in this lecture and more throughout our course.

Let’s try it!

Now that we’ve loaded the ggplot library into R, let’s test it out. But before we can put it to use, we’ll need to add one more vital component we’re still missing. The actual data!

Let’s start with the dataset we used last week: fish_size.csv and recreate the boxplot we made but with ggplot2 this time.

setwd("/home/kazanjian/Documents/R projects/ESS103/")
Warning: The working directory was changed to /home/kazanjian/Documents/R projects/ESS103 inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
fish = read.csv ("Data/fish_size.csv", header=T, sep=",")
head(fish)

In this dataset, we have 2 variables:

As the size of the fish is often dependent on its type, we will assume that former is the dependent variable (so y-axis) and the latter is the independent variable (so x-axis)

The simplest ggplot2 syntax follows the following logic: > ggplot (dataset, aes (x-axis, y-axis)) + graph_type() #you will often get a small pop-up in R studio while typing the command to help you choose the syntax for the graph type you want.

So for our specific example, we need to rewrite the above as:

ggplot(fish, aes(fish_types, size)) + geom_boxplot()

Alternatively, if we do not want to view our data as a boxplot but visualize all measurements as separate points, you can choose geom_point() as a graph type instead of *geom_boxplot()

ggplot(fish, aes(fish_types, size)) + geom_point()

Now let us make some modifications to the above graph. Assume our points are too small, so we want to make them bigger, and make them red to pop out! We can add these details within the parantheses of geom_point():

ggplot(fish, aes(fish_types, size)) + geom_point(size=3, color="red")

The flexibility of ggplot2 comes to the fore with the ability to add multiple visualization types at once. For the fun of it, let’s try to combine both previous graphs (boxplot and points) in one graph. We can even add some transparency to the points (alpha = 0 for full transparency to 1 for no transparency) to not fully shield the boxplots below them.

ggplot(fish, aes(fish_types, size)) + 
  geom_boxplot() + 
  geom_point(size=3, color="red", alpha = 0.2)

Our figure though is still missing a title, however. This can be added with the command labs. To also show our x and y-axes titles more professionally, we can use the commands xlab and ylab, respectively. Lastly, you can experiment with different color themes like black and white (bw), a dark theme, a minimal theme, etc. See below:

ggplot(fish, aes(fish_types, size)) + 
  geom_boxplot() + 
  geom_point(size=3, color="red", alpha = 0.2) + 
  labs(title= "Size of fishes by type", 
       x="Fish Types",
       y="Length in cm") +
  theme_bw()

Now we have a professional-looking graph that we can export and use anywhere.

How to export a graph

To export any graph produced by ggplot2, we can simply use the ggsave command, as follows: > ggsave(filename of image to be saved,device = filetype #(e.g. png, jpeg, tiff, png, bmp, svg, or pdf).

Unless otherwise specified, it defaults to last plot displayed.

ggsave("Fish sizes by type", device=jpeg)
Saving 7 x 7 in image

The image should now appear in your working directory.

Alternatively, you can also right-click on the generated image and select ‘Save image as…’. Codes executed in the console also appear in the ‘Plots’ tab in the bottom right window, where you can also graphically export them.

Time series and line graphs

Recall that when we had a continuous or time series data, then the most common type of graph to use was the line graph.

For this example, we will use the BOD dataset in the base R package.

head(BOD)

As you can see, we have 2 columns: 1 indicating time, the other oxygen demand. We want to plot the O2 demand vs time. As O2 is the dependent variable and time is the independent variable, the former goes onto the y-axis.

Replace geom_point() with geom_line() to draw a line instead of scatter points.

ggplot(BOD, aes(Time, demand))+
  geom_line() 

You can still show the points if you’d like to clarify when the measurements were made. To do so, you can use both geom_point() with geom_line() commands in your code.

ggplot(BOD, aes(Time, demand))+
  geom_line() +
  geom_point(aes(color="red", alpha = 0.6, size = 2), show.legend = F)

Visualization of multiparameter data

So far, we’ve only looked into 2 parameters (fish type vs size or one parameter vs time). What if we had several parameters that we wanted to visualize simultaneously?

Consider the dataset called CO2, which shows carbon dioxide uptake in grass plants from an experiment on the cold tolerance of the grass species Echinochloa crus-galli. The data has 5 columns as can be seen below:

print(CO2)

As you can see, this dataset has 1 independent numeric variable (conc), 1 dependent numeric variable (uptake), and 3 categorical variables (Plant, Type, and Treatment)

If we were to plot this like the first example, we’d plot concentration vs uptake. As uptake is the dependent variable, it goes to the y-axis. So we’ll have:

ggplot(CO2, aes(conc, uptake))+
  geom_point()

But what if we want to see a 3rd parameter (such as Treatment) also to investigate any relations with it too? We can add the treatment parameter to be represented by different shapes to the above graph.

ggplot(CO2, aes(conc, uptake, shape=Treatment))+
  geom_point()

As you see, we have the exact same graph produced but instead of the dots we have 2 different shapes, circle and triangle, representing the nonchilled and chilled treatments, respectively.

Finally, we want to add a 4th parameter as well: Plant. For this variable we will use different colors to distinguish the different plants in our dataset.

In the previous graph, things were already slightly difficult to distinguish due to the small sizes of the points. Now with the extra parameter, it might be even harder to analyze. So to improve the above graph, let’s make the dots slightly bigger and increase their transparency:

ggplot(CO2, aes(conc, uptake, color=Plant, shape=Treatment))+
  geom_point(size=4, alpha=0.6)

Facets

Sometimes, when you have a complex dataset with too many variables, you’ll reach the limit to how many of the variables you can add into a single graph while still keeping it simple enough to read, understand, or analyze.

In those cases, sometimes it is better to split the data into subsets and display them as multi panel plots. This functionality in ggplot2 is called facets.

Take the above example. Only now, we also want to add the Type as well into the graph. We want to do that by splitting the data into 2 panels, distinguished by the Type parameter.

ggplot(CO2, aes(conc, uptake, color=Plant, shape=Treatment))+
  geom_point(size=4, alpha=0.6)+
  facet_wrap(~Type)

Now to analyze, we can deduce that there is a clear distinction between the Quebec and Mississippi types, with uptake being relatively higher in the former. There is also a clear Treatment effect in Mississippi plants, with chilled plants having lower uptake than nonchilled plants. However, this distinction is less clear in the Quebec plants.

Finally, to complete our graph, we want to add a title and fix the axis labels:

ggplot(CO2, aes(conc, uptake, color=Plant, shape=Treatment))+
  geom_point(size=4, alpha=0.6)+
  facet_wrap(~Type)+
  labs (title="Uptake of CO2 per plant and treatment type", 
        x="concentration",
        y= "uptake") 

Try on your own

Select data(economics) This dataset was produced from US economic time series data available from https://fred.stlouisfed.org/. It is a data frame with 6 population variables and 574 rows.

First of all, view the first 6 rows of the dataset so you have an idea what it looks like.

Now plot the number of unemployed vs date. Make sure the dependent variable is on the y-axis. Add onto your figure the median duration of unemployment (uempmed) as size of the dots and the personal savings rate (psavert) as the color of the dots.

Export the figure. Analyze!

Tip: The figure should look like this: this

Thirsty for more? Here are some additional resources

There are a plethora of resources that you can find freely online for data visualization, in general and ggplot2, specifically. But here are a few good suggestions if you want to delve deeper into the topic:

LS0tCnRpdGxlOiAiRGF0YSB2aXN1YWxpemF0aW9uIgpzdWJ0aXRsZTogIkFtZXJpY2FuIFVuaXZlcnNpdHkgb2YgQXJtZW5pYSIKYXV0aG9yOiAiRVNTIDEwMyAtIFNwcmluZyAyMDI0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgohW0ltYWdlIHNvdXJjZTogdGhlbmV3c3RhY2tdKGh0dHBzOi8vY2RuLnRoZW5ld3N0YWNrLmlvL21lZGlhLzIwMjMvMDEvMjg1ZDY4ZGQtY2hhcnRzLTEwMjR4NTgxLmpwZykKCiMjIyBXaHkgdmlzdWFsaXplPwoKRGF0YSB2aXN1YWxpemF0aW9uIGlzIHRoZSBncmFwaGljYWwgcmVwcmVzZW50YXRpb24gb2YgaW5mb3JtYXRpb24gYW5kIGRhdGEuIEJ5IHVzaW5nIHZpc3VhbCBlbGVtZW50cyBsaWtlIGNoYXJ0cywgZ3JhcGhzLCBhbmQgbWFwcywgZGF0YSB2aXN1YWxpemF0aW9uIHRvb2xzIHByb3ZpZGUgYW4gYWNjZXNzaWJsZSB3YXkgdG8gc2VlIGFuZCB1bmRlcnN0YW5kIHRyZW5kcywgb3V0bGllcnMsIGFuZCBwYXR0ZXJucyBpbiBkYXRhLiBIaWRkZW4gd2l0aGluIHlvdXIgZGF0YSBsaWUgaW1wb3J0YW50IGluc2lnaHRzLiBCdXQgdGhlIGNoYWxsZW5nZSBpcyB0aGF0IHlvdSBjYW7igJl0IGFsd2F5cyBjb25uZWN0IHRoZSBkb3RzIGJ5IGxvb2tpbmcgYXQgcmF3IG51bWJlcnMgYWxvbmUuIFdoZW4geW91IGxvb2sgYXQgeW91ciBkYXRhIHByZXNlbnRlZCBpbiBhIHZpc3VhbCBmb3JtYXQsIHBhdHRlcm5zLCBjb25uZWN0aW9ucywgYW5kIG90aGVyIOKAnGEtaGHigJ0gaW5zaWdodHMgZW1lcmdlIHRoYXQgd291bGQgb3RoZXJ3aXNlIHJlbWFpbiBvdXQgb2Ygc2lnaHQuCgpUaGUgZWFybGllc3QgZm9ybSBvZiBkYXRhIHZpc3VhbGl6YXRpb24gY2FuIGJlIHRyYWNlZCBiYWNrIHRvIHRoZSBFZ3lwdGlhbnMgaW4gdGhlIHByZS0xN3RoIGNlbnR1cnksIGxhcmdlbHkgdXNlZCB0byBhc3Npc3QgaW4gbmF2aWdhdGlvbi4gQXMgdGltZSBwcm9ncmVzc2VkLCBwZW9wbGUgbGV2ZXJhZ2VkIGRhdGEgdmlzdWFsaXphdGlvbnMgZm9yIGJyb2FkZXIgYXBwbGljYXRpb25zLCBzdWNoIGFzIGluIGVjb25vbWljLCBzb2NpYWwsIGhlYWx0aCwgYW5kIGVudmlyb25tZW50YWwgZGlzY2lwbGluZXMuCgpUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgd2F5cyB0byB2aXN1YWxpemUgZGF0YS4gU29tZSBvZiB0aGUgbW9zdCBjb21tb24gdGVjaG5pcXVlcyBhcmU6CgotICAgKipUYWJsZXMqKjogVGhpcyBjb25zaXN0cyBvZiByb3dzIGFuZCBjb2x1bW5zIHVzZWQgdG8gY29tcGFyZSB2YXJpYWJsZXMuIFRhYmxlcyBjYW4gc2hvdyBhIGdyZWF0IGRlYWwgb2YgaW5mb3JtYXRpb24gaW4gYSBzdHJ1Y3R1cmVkIHdheSwgYnV0IHRoZXkgY2FuIGFsc28gb3ZlcndoZWxtIHVzZXJzIHRoYXQgYXJlIHNpbXBseSBsb29raW5nIGZvciBoaWdoLWxldmVsIHRyZW5kcy4KCi0gICAqKkJhciBncmFwaHMgYW5kIGJveHBsb3RzKio6IFRoZXNlIGFyZSB1c2VkIHRvIHJlcHJlc2VudCBhbmQgY29tcGFyZSBncm91cHMgb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBCb3hwbG90cyBpbiBhZGRpdGlvbiBhbHNvIHJlcHJlc2VudCBudW1lcm91cyBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFib3V0IGVhY2ggY2F0ZWdvcnkgbGlrZSBpdHMgbWVhbiBhbmQgcmFuZ2UuCgotICAgKipTY2F0dGVyIHBsb3RzKio6IFRoZXNlIHZpc3VhbHMgYXJlIGJlbmVmaWNpYWwgaW4gcmV2ZWxpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMsIGFuZCB0aGV5IGFyZSBjb21tb25seSB1c2VkIHdpdGhpbiByZWdyZXNzaW9uIGRhdGEgYW5hbHlzaXMuIEhvd2V2ZXIsIHRoZXNlIGNhbiBzb21ldGltZXMgYmUgY29uZnVzZWQgd2l0aCBidWJibGUgY2hhcnRzLCB3aGljaCBhcmUgdXNlZCB0byB2aXN1YWxpemUgdGhyZWUgdmFyaWFibGVzIHZpYSB0aGUgeC1heGlzLCB0aGUgeS1heGlzLCBhbmQgdGhlIHNpemUgb2YgdGhlIGJ1YmJsZS4KCi0gICAqKlBpZSBjaGFydHMgYW5kIHN0YWNrZWQgYmFyIGNoYXJ0cyoqOiBUaGVzZSBncmFwaHMgYXJlIGRpdmlkZWQgaW50byBzZWN0aW9ucyB0aGF0IHJlcHJlc2VudCBwYXJ0cyAocG9ydGlvbnMgb3IgcGVyY2VudGFnZXMpIG9mIGEgd2hvbGUuIFRoZXkgcHJvdmlkZSBhIHNpbXBsZSB3YXkgdG8gb3JnYW5pemUgZGF0YSBhbmQgY29tcGFyZSB0aGUgc2l6ZSBvZiBlYWNoIGNvbXBvbmVudCB0byBvbmUgb3RoZXIuCgotICAgKipMaW5lIGNoYXJ0cyBhbmQgYXJlYSBjaGFydHMqKjogVGhlc2UgdmlzdWFscyBzaG93IGNoYW5nZSBpbiBvbmUgb3IgbW9yZSBxdWFudGl0aWVzIGJ5IHBsb3R0aW5nIGEgc2VyaWVzIG9mIGRhdGEgcG9pbnRzIG92ZXIgdGltZSBhbmQgYXJlIGZyZXF1ZW50bHkgdXNlZCB3aXRoaW4gcHJlZGljdGl2ZSBhbmFseXRpY3MuIExpbmUgZ3JhcGhzIHV0aWxpemUgbGluZXMgdG8gZGVtb25zdHJhdGUgdGhlc2UgY2hhbmdlcyB3aGlsZSBhcmVhIGNoYXJ0cyBjb25uZWN0IGRhdGEgcG9pbnRzIHdpdGggbGluZSBzZWdtZW50cywgc3RhY2tpbmcgdmFyaWFibGVzIG9uIHRvcCBvZiBvbmUgYW5vdGhlciBhbmQgdXNpbmcgY29sb3IgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiB2YXJpYWJsZXMuCgotICAgKipIaXN0b2dyYW1zKio6IFRoaXMgZ3JhcGggcGxvdHMgYSBkaXN0cmlidXRpb24gb2YgbnVtYmVycyB1c2luZyBhIGJhciBjaGFydCAod2l0aCBubyBzcGFjZXMgYmV0d2VlbiB0aGUgYmFycyksIHJlcHJlc2VudGluZyB0aGUgcXVhbnRpdHkgb2YgZGF0YSAob3IgZnJlcXVlbmN5KSB0aGF0IGZhbGxzIHdpdGhpbiBhIHBhcnRpY3VsYXIgcmFuZ2UuIFRoaXMgdmlzdWFsIG1ha2VzIGl0IGVhc3kgZm9yIGFuIGVuZCB1c2VyIHRvIGlkZW50aWZ5IG91dGxpZXJzIHdpdGhpbiBhIGdpdmVuIGRhdGFzZXQuCgoqKkltcG9ydGFudCoqOiBCeSBjb252ZW50aW9uLCB0aGUgeC1heGlzIGlzIHRoZSAqKmluZGVwZW5kZW50IHZhcmlhYmxlKiogYW5kIHRoZSAqKmRlcGVuZGVudCB2YXJpYWJsZSoqIGlzIHBsb3R0ZWQgb24gdGhlIHktYXhpcy4KClNvIGZhciB3ZSBoYXZlIGJlZW4gdXNpbmcgYmFzZSBSIHZpc3VhbGl6YXRpb24gdG9vbHMgKGUuZy4gd2hlbiB5b3UgdXNlZCB0aGUgY29tbWFuZDogKnBsb3QqKS4gSG93ZXZlciwgdGhlcmUgYXJlIHZhcmlvdXMgb3RoZXIgcGFja2FnZXMgdGhhdCB3ZSBjYW4gdXNlIHRvIHByb2R1Y2UgbmljZXIgYW5kIG1vcmUgc29waGlzdGljYXRlZCBncmFwaHMuIE9uZSBvZiB0aGUgbW9zdCBwb3B1bGFyIG9mIHN1Y2ggcGFja2FnZXMgaXMgY2FsbGVkICoqKmdncGxvdDIqKiouCgojIyMgQnV0IHdoYXQgYXJlIFIgcGFja2FnZXM/CgpPbmUgb2YgdGhlIHByaW1hcnkgcmVhc29ucyBmb3IgUuKAmXMgcG9wdWxhcml0eSBpcyBpdHMgZXh0ZW5zaXZlIHBhY2thZ2UgZWNvc3lzdGVtLiBPbiBS4oCZcyBtYWluIHBhY2thZ2UgcmVwb3NpdG9yeTogW0NvbXByZWhlbnNpdmUgUiBBcmNoaXZlIE5ldHdvcmtdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLykgKENSQU4pIGFsb25lIHlvdSBoYXZlIG92ZXIgMTAsMDAwIHBhY2thZ2VzIGF2YWlsYWJsZSB0byBjaG9vc2UgZnJvbS4gWW91IGNhbiBzZWUgdGhlIGxpc3Qgb2YgYXZhaWxhYmxlIHBhY2thZ2VzIG9uIENSQU4gW2hlcmVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9hdmFpbGFibGVfcGFja2FnZXNfYnlfbmFtZS5odG1sKS4gWWV0LCB3aGVuIHlvdSBmaXJzdCBpbnN0YWxsIFIgeW91IG9ubHkgZ2V0IGEgdmVyeSBsaW1pdGVkIHNldCBvZiBjb3JlIHBhY2thZ2VzIOKAnG91dCBvZiB0aGUgYm944oCdLiBBbnkgZnVydGhlciBwYWNrYWdlcyB0aGF0IHlvdeKAmWQgbGlrZSB0byB1c2UgeW91IGhhdmUgdG8gaW5zdGFsbCB5b3Vyc2VsZi4KCiMjIyBJbnN0YWxsaW5nIHBhY2thZ2VzCgpUbyBiZSBhYmxlIHRvIHVzZSBhIChub24tYmFzZSkgcGFja2FnZSBmb3IgdGhlIGZpcnN0IHRpbWUsIHdlIHdpbGwgbmVlZCB0byBmaXJzdCBpbnN0YWxsIGl0IG9uIG91ciBzeXN0ZW0uIEluc3RhbGxpbmcgcGFja2FnZXMgZnJvbSBDUkFOIGNvdWxkbuKAmXQgYmUgZWFzaWVyISBTaW1wbHkgdHlwZSBpbnN0YWxsLnBhY2thZ2VzKCkgd2l0aCB0aGUgbmFtZSBvZiB5b3VyIGRlc2lyZWQgcGFja2FnZSBpbiBxdW90ZXMgYXMgZmlyc3QgYXJndW1lbnQuCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCmBgYAoKTm90ZTogQWx0aG91Z2ggSSBoYXZlIGluY2x1ZGVkIGFuZCBleGVjdXRlZCB0aGUgY29kZSB3aXRoaW4gbXkgbm90ZWJvb2sgaGVyZSAodG8gc2hvdyB5b3UgaG93IGl0IGlzIGRvbmUpLCBpdCBpcyB1c3VhbGx5IGFkdmlzYWJsZSB0byBub3QgaW5jbHVkZSB0aGlzIGluIFIgbm90ZWJvb2tzIGJ1dCBkaXJlY3RseSBpbiB5b3VyIGNvbnNvbGUgKHRoZSBib3ggYXQgdGhlIGxvd2VyIGxlZnQgY29ybmVyIHVzdWFsbHkgaW4gUlN0dWRpbykgYXMKCjEpICAqKllvdSB3aWxsIG9ubHkgbmVlZCB0byBpbnN0YWxsIGl0IG9uY2Ugb24gZWFjaCBjb21wdXRlciB5b3UgdXNlLioqCjIpICBJdCB3aWxsIG9mdGVuIHByb2R1Y2UgdmVyeSBsb25nIGNvZGVzIHRoYXQgd2lsbCBtYWtlIHlvdXIgaHRtbCAob3IgcGRmKSBvdXRwdXRzIHZlcnkgbWVzc3kuCgpPbmNlIGEgcGFja2FnZSBpcyBpbnN0YWxsZWQgb24geW91ciBzeXN0ZW0sIHlvdSBjYW4gbG9hZCBpdCB0byBSIGJ5IHVzaW5nIHRoZSBjb21tYW5kOiBsaWJyYXJ5KCpwYWNrYWdlIG5hbWUqKS4gKipZb3Ugd2lsbCBoYXZlIHRvIGRvIHRoaXMgZXZlcnkgdGltZSBhZnRlciB5b3UgcmVzdGFydCBSKiouIFRodXMsIHVubGlrZSB0aGUgKmluc3RhbGwucGFja2FnZXMqIGNvbW1hbmQsIGl0IGlzIHJlY29tbWVuZGVkIHRvIGluY2x1ZGUgdGhpcyBhdCB0aGUgdG9wIG9mIGVhY2ggb2YgeW91ciBub3RlYm9va3MsIHRvIG1ha2Ugc3VyZSB0aGUgcGFja2FnZXMgeW91IG5lZWQgYXJlIGxvYWRlZC4KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKIyMjIGdncGxvdDIKCipnZ3Bsb3QyKiBpcyBhIHBvd2VyZnVsIGRhdGEgdmlzdWFsaXphdGlvbiBwYWNrYWdlIGluIHRoZSBSIHByb2dyYW1taW5nIGxhbmd1YWdlLiBJdCBpcyBiYXNlZCBvbiB0aGUgZ3JhbW1hciBvZiBncmFwaGljcywgd2hpY2ggaXMgYSB3YXkgb2YgZGVzY3JpYmluZyBhbmQgYnVpbGRpbmcgZ3JhcGhzIHVzaW5nIGEgc3RydWN0dXJlZCBhcHByb2FjaC4gKmdncGxvdDIqIGFsbG93cyB5b3UgdG8gY3JlYXRlIGEgd2lkZSB2YXJpZXR5IG9mIHBsb3RzIGFuZCBncmFwaGljcywgaW5jbHVkaW5nIHNjYXR0ZXIgcGxvdHMsIGJhciBwbG90cywgbGluZSBwbG90cywgaGlzdG9ncmFtcywgYW5kIG1vcmUsIHdpdGggYSBoaWdoIGRlZ3JlZSBvZiBjdXN0b21pemF0aW9uIGFuZCBmbGV4aWJpbGl0eS4gSXQgaXMgbm93IG92ZXIgMTAgeWVhcnMgb2xkIGFuZCBpcyB3aWRlbHkgdXNlZCBpbiB0aGUgZGF0YSBzY2llbmNlIGFuZCBzdGF0aXN0aWNhbCBjb21tdW5pdGllcyBmb3IgY3JlYXRpbmcgdmlzdWFsbHkgYXBwZWFsaW5nIGFuZCBpbmZvcm1hdGl2ZSBwbG90cyBmcm9tIGRhdGEuCgpXaGlsZSBpdCBpcyBzbGlnaHRseSBtb3JlIGNvbXBsZXggdG8gZXhlY3V0ZSB0aGFuIHRoZSBzaW1wbGUgcGxvdHMgaW4gYmFzZSBSLCBvbmNlIHlvdSB1bmRlcnN0YW5kIGl0cyBzeW50YXggKGJ5IHByYWN0aWNpbmchKSB5b3Ugd2lsbCBnZXQgdGhlIGhhbmcgb2YgaXQgcXVpY2tseS4gSXQgaXMgYmVzdCB0byBpbWFnaW5lIGhvdyAqZ2dwbG90Miogd29ya3MgYXMgbWFraW5nIGdyYXBocyB3aXRoIGRpZmZlcmVudCBsYXllcnMuIEluIG1vc3QgY2FzZXMgeW91IHN0YXJ0IHdpdGggZ2dwbG90KCksIHN1cHBseSBhIGRhdGFzZXQgYW5kIGFlc3RoZXRpYyBtYXBwaW5nICh3aXRoIGFlcygpKSAqVGhpcyB3aWxsIGJlIHRoZSBiYWNrZ3JvdW5kIGFuZCBheGVzIG9mIHlvdXIgZ3JhcGgqLiBZb3UgdGhlbiBhZGQgb24gbGF5ZXJzIChsaWtlIGdlb21fcG9pbnQoKSBvciBnZW9tX2hpc3RvZ3JhbSgpKSAqVGhlc2Ugd2lsbCBiZSB0aGUgcG9pbnRzLCBsaW5lcyBvciBiYXJzIG9uIHRvcCBvZiB5b3VyIGJhY2tncm91bmQqLCBzY2FsZXMgKGxpa2Ugc2NhbGVfY29sb3VyX2JyZXdlcigpKSwgZmFjZXRpbmcgc3BlY2lmaWNhdGlvbnMgKGxpa2UgZmFjZXRfd3JhcCgpKSBhbmQgY29vcmRpbmF0ZSBzeXN0ZW1zIChsaWtlIGNvb3JkX2ZsaXAoKSkuIFdlIHdpbGwgY292ZXIgc29tZSBvZiB0aGVzZSBpbiB0aGlzIGxlY3R1cmUgYW5kIG1vcmUgdGhyb3VnaG91dCBvdXIgY291cnNlLiAKCiMjIyBMZXQncyB0cnkgaXQhCk5vdyB0aGF0IHdlJ3ZlIGxvYWRlZCB0aGUgZ2dwbG90IGxpYnJhcnkgaW50byBSLCBsZXQncyB0ZXN0IGl0IG91dC4KQnV0IGJlZm9yZSB3ZSBjYW4gcHV0IGl0IHRvIHVzZSwgd2UnbGwgbmVlZCB0byBhZGQgb25lIG1vcmUgdml0YWwgY29tcG9uZW50IHdlJ3JlIHN0aWxsIG1pc3NpbmcuIFRoZSBhY3R1YWwgZGF0YSEKCkxldCdzIHN0YXJ0IHdpdGggdGhlIGRhdGFzZXQgd2UgdXNlZCBsYXN0IHdlZWs6IGZpc2hfc2l6ZS5jc3YgYW5kIHJlY3JlYXRlIHRoZSBib3hwbG90IHdlIG1hZGUgYnV0IHdpdGggKmdncGxvdDIqIHRoaXMgdGltZS4KYGBge3J9CnNldHdkKCIvaG9tZS9rYXphbmppYW4vRG9jdW1lbnRzL1IgcHJvamVjdHMvRVNTMTAzLyIpCmZpc2ggPSByZWFkLmNzdiAoIkRhdGEvZmlzaF9zaXplLmNzdiIsIGhlYWRlcj1ULCBzZXA9IiwiKQpoZWFkKGZpc2gpCmBgYAoKSW4gdGhpcyBkYXRhc2V0LCB3ZSBoYXZlIDIgdmFyaWFibGVzOgoKLSAxIGNvbnRpbnVlcyB2YXJpYWJsZTogc2l6ZSAob3IgbGVuZ3RoKSBvZiB0aGUgZmlzaAotIDEgY2F0ZWdvcmljYWwgdmFyaWFibGU6IHRoZSB0eXBlIG9mIHRoZSBmaXNoCgpBcyB0aGUgc2l6ZSBvZiB0aGUgZmlzaCBpcyBvZnRlbiBkZXBlbmRlbnQgb24gaXRzIHR5cGUsIHdlIHdpbGwgYXNzdW1lIHRoYXQgZm9ybWVyIGlzIHRoZSAqKmRlcGVuZGVudCB2YXJpYWJsZSoqIChzbyB5LWF4aXMpIGFuZCB0aGUgbGF0dGVyIGlzIHRoZSAqKmluZGVwZW5kZW50IHZhcmlhYmxlKiogKHNvIHgtYXhpcykKClRoZSBzaW1wbGVzdCBnZ3Bsb3QyIHN5bnRheCBmb2xsb3dzIHRoZSBmb2xsb3dpbmcgbG9naWM6Cj4gZ2dwbG90IChkYXRhc2V0LCBhZXMgKHgtYXhpcywgeS1heGlzKSkgKyBncmFwaF90eXBlKCkgI3lvdSB3aWxsIG9mdGVuIGdldCBhIHNtYWxsIHBvcC11cCBpbiBSIHN0dWRpbyB3aGlsZSB0eXBpbmcgdGhlIGNvbW1hbmQgdG8gaGVscCB5b3UgY2hvb3NlIHRoZSBzeW50YXggZm9yIHRoZSBncmFwaCB0eXBlIHlvdSB3YW50LgoKU28gZm9yIG91ciBzcGVjaWZpYyBleGFtcGxlLCB3ZSBuZWVkIHRvIHJld3JpdGUgdGhlIGFib3ZlIGFzOgoKYGBge3J9CmdncGxvdChmaXNoLCBhZXMoZmlzaF90eXBlcywgc2l6ZSkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCkFsdGVybmF0aXZlbHksIGlmIHdlIGRvIG5vdCB3YW50IHRvIHZpZXcgb3VyIGRhdGEgYXMgYSBib3hwbG90IGJ1dCB2aXN1YWxpemUgYWxsIG1lYXN1cmVtZW50cyBhcyBzZXBhcmF0ZSBwb2ludHMsIHlvdSBjYW4gY2hvb3NlICpnZW9tX3BvaW50KCkqIGFzIGEgZ3JhcGggdHlwZSBpbnN0ZWFkIG9mICpnZW9tX2JveHBsb3QoKQoKYGBge3J9CmdncGxvdChmaXNoLCBhZXMoZmlzaF90eXBlcywgc2l6ZSkpICsgZ2VvbV9wb2ludCgpCmBgYApOb3cgbGV0IHVzIG1ha2Ugc29tZSBtb2RpZmljYXRpb25zIHRvIHRoZSBhYm92ZSBncmFwaC4KQXNzdW1lIG91ciBwb2ludHMgYXJlIHRvbyBzbWFsbCwgc28gd2Ugd2FudCB0byBtYWtlIHRoZW0gYmlnZ2VyLCBhbmQgbWFrZSB0aGVtIHJlZCB0byBwb3Agb3V0IQpXZSBjYW4gYWRkIHRoZXNlIGRldGFpbHMgd2l0aGluIHRoZSBwYXJhbnRoZXNlcyBvZiBnZW9tX3BvaW50KCk6CgpgYGB7cn0KZ2dwbG90KGZpc2gsIGFlcyhmaXNoX3R5cGVzLCBzaXplKSkgKyBnZW9tX3BvaW50KHNpemU9MywgY29sb3I9InJlZCIpCmBgYAoKVGhlIGZsZXhpYmlsaXR5IG9mICpnZ3Bsb3QyKiBjb21lcyB0byB0aGUgZm9yZSB3aXRoIHRoZSBhYmlsaXR5IHRvIGFkZCBtdWx0aXBsZSB2aXN1YWxpemF0aW9uIHR5cGVzIGF0IG9uY2UuIEZvciB0aGUgZnVuIG9mIGl0LCBsZXQncyB0cnkgdG8gY29tYmluZSBib3RoIHByZXZpb3VzIGdyYXBocyAoYm94cGxvdCBhbmQgcG9pbnRzKSBpbiBvbmUgZ3JhcGguIFdlIGNhbiBldmVuIGFkZCBzb21lIHRyYW5zcGFyZW5jeSB0byB0aGUgcG9pbnRzIChhbHBoYSA9IDAgZm9yIGZ1bGwgdHJhbnNwYXJlbmN5IHRvIDEgZm9yIG5vIHRyYW5zcGFyZW5jeSkgdG8gbm90IGZ1bGx5IHNoaWVsZCB0aGUgYm94cGxvdHMgYmVsb3cgdGhlbS4KCmBgYHtyfQpnZ3Bsb3QoZmlzaCwgYWVzKGZpc2hfdHlwZXMsIHNpemUpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9wb2ludChzaXplPTMsIGNvbG9yPSJyZWQiLCBhbHBoYSA9IDAuMikKYGBgCgpPdXIgZmlndXJlIHRob3VnaCBpcyBzdGlsbCBtaXNzaW5nIGEgdGl0bGUsIGhvd2V2ZXIuIFRoaXMgY2FuIGJlIGFkZGVkIHdpdGggdGhlIGNvbW1hbmQgKmxhYnMqLiBUbyBhbHNvIHNob3cgb3VyIHggYW5kIHktYXhlcyB0aXRsZXMgbW9yZSBwcm9mZXNzaW9uYWxseSwgd2UgY2FuIHVzZSB0aGUgY29tbWFuZHMgKnhsYWIqIGFuZCAqeWxhYiosIHJlc3BlY3RpdmVseS4gTGFzdGx5LCB5b3UgY2FuIGV4cGVyaW1lbnQgd2l0aCBkaWZmZXJlbnQgY29sb3IgdGhlbWVzIGxpa2UgYmxhY2sgYW5kIHdoaXRlIChidyksIGEgZGFyayB0aGVtZSwgYSBtaW5pbWFsIHRoZW1lLCBldGMuIFNlZSBiZWxvdzoKCmBgYHtyfQpnZ3Bsb3QoZmlzaCwgYWVzKGZpc2hfdHlwZXMsIHNpemUpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9wb2ludChzaXplPTMsIGNvbG9yPSJyZWQiLCBhbHBoYSA9IDAuMikgKyAKICBsYWJzKHRpdGxlPSAiU2l6ZSBvZiBmaXNoZXMgYnkgdHlwZSIsIAogICAgICAgeD0iRmlzaCBUeXBlcyIsCiAgICAgICB5PSJMZW5ndGggaW4gY20iKSArCiAgdGhlbWVfYncoKQpgYGAKTm93IHdlIGhhdmUgYSBwcm9mZXNzaW9uYWwtbG9va2luZyBncmFwaCB0aGF0IHdlIGNhbiBleHBvcnQgYW5kIHVzZSBhbnl3aGVyZS4KCiMjIyBIb3cgdG8gZXhwb3J0IGEgZ3JhcGgKClRvIGV4cG9ydCBhbnkgZ3JhcGggcHJvZHVjZWQgYnkgKmdncGxvdDIqLCB3ZSBjYW4gc2ltcGx5IHVzZSB0aGUgKmdnc2F2ZSogY29tbWFuZCwgYXMgZm9sbG93czoKPiBnZ3NhdmUoZmlsZW5hbWUgb2YgaW1hZ2UgdG8gYmUgc2F2ZWQsZGV2aWNlID0gZmlsZXR5cGUgIyhlLmcuIHBuZywganBlZywgdGlmZiwgcG5nLCBibXAsIHN2Zywgb3IgcGRmKS4gCgpVbmxlc3Mgb3RoZXJ3aXNlIHNwZWNpZmllZCwgaXQgZGVmYXVsdHMgdG8gbGFzdCBwbG90IGRpc3BsYXllZC4KCmBgYHtyfQpnZ3NhdmUoIkZpc2ggc2l6ZXMgYnkgdHlwZSIsIGRldmljZT1qcGVnKQpgYGAKClRoZSBpbWFnZSBzaG91bGQgbm93IGFwcGVhciBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiAKCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gYWxzbyByaWdodC1jbGljayBvbiB0aGUgZ2VuZXJhdGVkIGltYWdlIGFuZCBzZWxlY3QgJypTYXZlIGltYWdlIGFzLi4uKicuIENvZGVzIGV4ZWN1dGVkIGluIHRoZSBjb25zb2xlIGFsc28gYXBwZWFyIGluIHRoZSAnUGxvdHMnIHRhYiBpbiB0aGUgYm90dG9tIHJpZ2h0IHdpbmRvdywgd2hlcmUgeW91IGNhbiBhbHNvIGdyYXBoaWNhbGx5IGV4cG9ydCB0aGVtLgoKIyMjIFRpbWUgc2VyaWVzIGFuZCBsaW5lIGdyYXBocwoKUmVjYWxsIHRoYXQgd2hlbiB3ZSBoYWQgYSBjb250aW51b3VzIG9yIHRpbWUgc2VyaWVzIGRhdGEsIHRoZW4gdGhlIG1vc3QgY29tbW9uIHR5cGUgb2YgZ3JhcGggdG8gdXNlIHdhcyB0aGUgbGluZSBncmFwaC4KCkZvciB0aGlzIGV4YW1wbGUsIHdlIHdpbGwgdXNlIHRoZSAqQk9EKiBkYXRhc2V0IGluIHRoZSBiYXNlIFIgcGFja2FnZS4KCmBgYHtyfQpoZWFkKEJPRCkKYGBgCgpBcyB5b3UgY2FuIHNlZSwgd2UgaGF2ZSAyIGNvbHVtbnM6IDEgaW5kaWNhdGluZyB0aW1lLCB0aGUgb3RoZXIgb3h5Z2VuIGRlbWFuZC4gV2Ugd2FudCB0byBwbG90IHRoZSBPMiBkZW1hbmQgdnMgdGltZS4gQXMgTzIgaXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgdGltZSBpcyB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUsIHRoZSBmb3JtZXIgZ29lcyBvbnRvIHRoZSB5LWF4aXMuCgpSZXBsYWNlICpnZW9tX3BvaW50KCkqIHdpdGggKmdlb21fbGluZSgpKiB0byBkcmF3IGEgbGluZSBpbnN0ZWFkIG9mIHNjYXR0ZXIgcG9pbnRzLgoKYGBge3J9CmdncGxvdChCT0QsIGFlcyhUaW1lLCBkZW1hbmQpKSsKICBnZW9tX2xpbmUoKSAKYGBgCllvdSBjYW4gc3RpbGwgc2hvdyB0aGUgcG9pbnRzIGlmIHlvdSdkIGxpa2UgdG8gY2xhcmlmeSB3aGVuIHRoZSBtZWFzdXJlbWVudHMgd2VyZSBtYWRlLiBUbyBkbyBzbywgeW91IGNhbiB1c2UgYm90aCAgKmdlb21fcG9pbnQoKSogd2l0aCAqZ2VvbV9saW5lKCkqIGNvbW1hbmRzIGluIHlvdXIgY29kZS4KCmBgYHtyfQpnZ3Bsb3QoQk9ELCBhZXMoVGltZSwgZGVtYW5kKSkrCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPSJyZWQiLCBhbHBoYSA9IDAuNiwgc2l6ZSA9IDIpLCBzaG93LmxlZ2VuZCA9IEYpCmBgYAoKCgojIyMgVmlzdWFsaXphdGlvbiBvZiBtdWx0aXBhcmFtZXRlciBkYXRhCgpTbyBmYXIsIHdlJ3ZlIG9ubHkgbG9va2VkIGludG8gMiBwYXJhbWV0ZXJzIChmaXNoIHR5cGUgdnMgc2l6ZSBvciBvbmUgcGFyYW1ldGVyIHZzIHRpbWUpLiBXaGF0IGlmIHdlIGhhZCBzZXZlcmFsIHBhcmFtZXRlcnMgdGhhdCB3ZSB3YW50ZWQgdG8gdmlzdWFsaXplIHNpbXVsdGFuZW91c2x5PwoKQ29uc2lkZXIgdGhlIGRhdGFzZXQgY2FsbGVkIENPMiwgd2hpY2ggc2hvd3MgY2FyYm9uIGRpb3hpZGUgdXB0YWtlIGluIGdyYXNzIHBsYW50cyBmcm9tIGFuIGV4cGVyaW1lbnQgb24gdGhlIGNvbGQgdG9sZXJhbmNlIG9mIHRoZSBncmFzcyBzcGVjaWVzICpFY2hpbm9jaGxvYSBjcnVzLWdhbGxpKi4gVGhlIGRhdGEgaGFzIDUgY29sdW1ucyBhcyBjYW4gYmUgc2VlbiBiZWxvdzoKCmBgYHtyfQpwcmludChDTzIpCmBgYAoKQXMgeW91IGNhbiBzZWUsIHRoaXMgZGF0YXNldCBoYXMgMSBpbmRlcGVuZGVudCBudW1lcmljIHZhcmlhYmxlIChjb25jKSwgMSBkZXBlbmRlbnQgbnVtZXJpYyB2YXJpYWJsZSAodXB0YWtlKSwgYW5kIDMgY2F0ZWdvcmljYWwgdmFyaWFibGVzIChQbGFudCwgVHlwZSwgYW5kIFRyZWF0bWVudCkKCklmIHdlIHdlcmUgdG8gcGxvdCB0aGlzIGxpa2UgdGhlIGZpcnN0IGV4YW1wbGUsIHdlJ2QgcGxvdCBjb25jZW50cmF0aW9uIHZzIHVwdGFrZS4KQXMgdXB0YWtlIGlzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIGl0IGdvZXMgdG8gdGhlIHktYXhpcy4KU28gd2UnbGwgaGF2ZToKYGBge3J9CmdncGxvdChDTzIsIGFlcyhjb25jLCB1cHRha2UpKSsKICBnZW9tX3BvaW50KCkKYGBgCkJ1dCB3aGF0IGlmIHdlIHdhbnQgdG8gc2VlIGEgM3JkIHBhcmFtZXRlciAoc3VjaCBhcyBUcmVhdG1lbnQpIGFsc28gdG8gaW52ZXN0aWdhdGUgYW55IHJlbGF0aW9ucyB3aXRoIGl0IHRvbz8KV2UgY2FuIGFkZCB0aGUgdHJlYXRtZW50IHBhcmFtZXRlciB0byBiZSByZXByZXNlbnRlZCBieSBkaWZmZXJlbnQgc2hhcGVzIHRvIHRoZSBhYm92ZSBncmFwaC4KCmBgYHtyfQpnZ3Bsb3QoQ08yLCBhZXMoY29uYywgdXB0YWtlLCBzaGFwZT1UcmVhdG1lbnQpKSsKICBnZW9tX3BvaW50KCkKYGBgCkFzIHlvdSBzZWUsIHdlIGhhdmUgdGhlIGV4YWN0IHNhbWUgZ3JhcGggcHJvZHVjZWQgYnV0IGluc3RlYWQgb2YgdGhlIGRvdHMgd2UgaGF2ZSAyIGRpZmZlcmVudCBzaGFwZXMsIGNpcmNsZSBhbmQgdHJpYW5nbGUsIHJlcHJlc2VudGluZyB0aGUgbm9uY2hpbGxlZCBhbmQgY2hpbGxlZCB0cmVhdG1lbnRzLCByZXNwZWN0aXZlbHkuIAoKCkZpbmFsbHksIHdlIHdhbnQgdG8gYWRkIGEgNHRoIHBhcmFtZXRlciBhcyB3ZWxsOiAqUGxhbnQqLgpGb3IgdGhpcyB2YXJpYWJsZSB3ZSB3aWxsIHVzZSBkaWZmZXJlbnQgY29sb3JzIHRvIGRpc3Rpbmd1aXNoIHRoZSBkaWZmZXJlbnQgcGxhbnRzIGluIG91ciBkYXRhc2V0LgoKSW4gdGhlIHByZXZpb3VzIGdyYXBoLCB0aGluZ3Mgd2VyZSBhbHJlYWR5IHNsaWdodGx5IGRpZmZpY3VsdCB0byBkaXN0aW5ndWlzaCBkdWUgdG8gdGhlIHNtYWxsIHNpemVzIG9mIHRoZSBwb2ludHMuIE5vdyB3aXRoIHRoZSBleHRyYSBwYXJhbWV0ZXIsIGl0IG1pZ2h0IGJlIGV2ZW4gaGFyZGVyIHRvIGFuYWx5emUuClNvIHRvIGltcHJvdmUgdGhlIGFib3ZlIGdyYXBoLCBsZXQncyBtYWtlIHRoZSBkb3RzIHNsaWdodGx5IGJpZ2dlciBhbmQgaW5jcmVhc2UgdGhlaXIgdHJhbnNwYXJlbmN5OgoKYGBge3J9CmdncGxvdChDTzIsIGFlcyhjb25jLCB1cHRha2UsIGNvbG9yPVBsYW50LCBzaGFwZT1UcmVhdG1lbnQpKSsKICBnZW9tX3BvaW50KHNpemU9NCwgYWxwaGE9MC42KQpgYGAKIyMjIEZhY2V0cwpTb21ldGltZXMsIHdoZW4geW91IGhhdmUgYSBjb21wbGV4IGRhdGFzZXQgd2l0aCB0b28gbWFueSB2YXJpYWJsZXMsICB5b3UnbGwgcmVhY2ggdGhlIGxpbWl0IHRvIGhvdyBtYW55IG9mIHRoZSB2YXJpYWJsZXMgeW91IGNhbiBhZGQgaW50byBhIHNpbmdsZSBncmFwaCB3aGlsZSBzdGlsbCBrZWVwaW5nIGl0IHNpbXBsZSBlbm91Z2ggdG8gcmVhZCwgdW5kZXJzdGFuZCwgb3IgYW5hbHl6ZS4KCkluIHRob3NlIGNhc2VzLCBzb21ldGltZXMgaXQgaXMgYmV0dGVyIHRvIHNwbGl0IHRoZSBkYXRhIGludG8gc3Vic2V0cyBhbmQgZGlzcGxheSB0aGVtIGFzIG11bHRpIHBhbmVsIHBsb3RzLiBUaGlzIGZ1bmN0aW9uYWxpdHkgaW4gKmdncGxvdDIqIGlzIGNhbGxlZCAqZmFjZXRzKi4KClRha2UgdGhlIGFib3ZlIGV4YW1wbGUuIE9ubHkgbm93LCB3ZSBhbHNvIHdhbnQgdG8gYWRkIHRoZSAqVHlwZSogYXMgd2VsbCBpbnRvIHRoZSBncmFwaC4gV2Ugd2FudCB0byBkbyB0aGF0IGJ5IHNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIDIgcGFuZWxzLCBkaXN0aW5ndWlzaGVkIGJ5IHRoZSAqVHlwZSogcGFyYW1ldGVyLgoKYGBge3J9CmdncGxvdChDTzIsIGFlcyhjb25jLCB1cHRha2UsIGNvbG9yPVBsYW50LCBzaGFwZT1UcmVhdG1lbnQpKSsKICBnZW9tX3BvaW50KHNpemU9NCwgYWxwaGE9MC42KSsKICBmYWNldF93cmFwKH5UeXBlKQpgYGAKTm93IHRvIGFuYWx5emUsIHdlIGNhbiBkZWR1Y2UgdGhhdCB0aGVyZSBpcyBhIGNsZWFyIGRpc3RpbmN0aW9uIGJldHdlZW4gdGhlIFF1ZWJlYyBhbmQgTWlzc2lzc2lwcGkgdHlwZXMsIHdpdGggdXB0YWtlIGJlaW5nIHJlbGF0aXZlbHkgaGlnaGVyIGluIHRoZSBmb3JtZXIuIFRoZXJlIGlzIGFsc28gYSBjbGVhciBUcmVhdG1lbnQgZWZmZWN0IGluIE1pc3Npc3NpcHBpIHBsYW50cywgd2l0aCBjaGlsbGVkIHBsYW50cyBoYXZpbmcgbG93ZXIgdXB0YWtlIHRoYW4gbm9uY2hpbGxlZCBwbGFudHMuIEhvd2V2ZXIsIHRoaXMgZGlzdGluY3Rpb24gaXMgbGVzcyBjbGVhciBpbiB0aGUgUXVlYmVjIHBsYW50cy4gCgpGaW5hbGx5LCB0byBjb21wbGV0ZSBvdXIgZ3JhcGgsIHdlIHdhbnQgdG8gYWRkIGEgdGl0bGUgYW5kIGZpeCB0aGUgYXhpcyBsYWJlbHM6CmBgYHtyfQpnZ3Bsb3QoQ08yLCBhZXMoY29uYywgdXB0YWtlLCBjb2xvcj1QbGFudCwgc2hhcGU9VHJlYXRtZW50KSkrCiAgZ2VvbV9wb2ludChzaXplPTQsIGFscGhhPTAuNikrCiAgZmFjZXRfd3JhcCh+VHlwZSkrCiAgbGFicyAodGl0bGU9IlVwdGFrZSBvZiBDTzIgcGVyIHBsYW50IGFuZCB0cmVhdG1lbnQgdHlwZSIsIAogICAgICAgIHg9ImNvbmNlbnRyYXRpb24iLAogICAgICAgIHk9ICJ1cHRha2UiKSAKYGBgCgojIyMgVHJ5IG9uIHlvdXIgb3duCgpTZWxlY3QgZGF0YShlY29ub21pY3MpClRoaXMgZGF0YXNldCB3YXMgcHJvZHVjZWQgZnJvbSBVUyBlY29ub21pYyB0aW1lIHNlcmllcyBkYXRhIGF2YWlsYWJsZSBmcm9tIGh0dHBzOi8vZnJlZC5zdGxvdWlzZmVkLm9yZy8uIApJdCBpcyBhIGRhdGEgZnJhbWUgd2l0aCA2IHBvcHVsYXRpb24gdmFyaWFibGVzIGFuZCA1NzQgcm93cy4KCkZpcnN0IG9mIGFsbCwgdmlldyB0aGUgZmlyc3QgNiByb3dzIG9mIHRoZSBkYXRhc2V0IHNvIHlvdSBoYXZlIGFuIGlkZWEgd2hhdCBpdCBsb29rcyBsaWtlLgoKTm93IHBsb3QgdGhlIG51bWJlciBvZiB1bmVtcGxveWVkIHZzIGRhdGUuIE1ha2Ugc3VyZSB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGlzIG9uIHRoZSB5LWF4aXMuIEFkZCBvbnRvIHlvdXIgZmlndXJlIHRoZSBtZWRpYW4gZHVyYXRpb24gb2YgdW5lbXBsb3ltZW50ICgqdWVtcG1lZCopIGFzIHNpemUgb2YgdGhlIGRvdHMgYW5kIHRoZSBwZXJzb25hbCBzYXZpbmdzIHJhdGUgKCpwc2F2ZXJ0KikgYXMgdGhlIGNvbG9yIG9mIHRoZSBkb3RzLiAKCkV4cG9ydCB0aGUgZmlndXJlLiBBbmFseXplIQoKVGlwOiBUaGUgZmlndXJlIHNob3VsZCBsb29rIGxpa2UgdGhpczogIVt0aGlzXShodHRwczovL2kuaW1ndXIuY29tL3U0d1RCMEsucG5nKQoKCiMjIyBUaGlyc3R5IGZvciBtb3JlPyBIZXJlIGFyZSBzb21lIGFkZGl0aW9uYWwgcmVzb3VyY2VzCgpUaGVyZSBhcmUgYSBwbGV0aG9yYSBvZiByZXNvdXJjZXMgdGhhdCB5b3UgY2FuIGZpbmQgZnJlZWx5IG9ubGluZSBmb3IgZGF0YSB2aXN1YWxpemF0aW9uLCBpbiBnZW5lcmFsIGFuZCBnZ3Bsb3QyLCBzcGVjaWZpY2FsbHkuIEJ1dCBoZXJlIGFyZSBhIGZldyBnb29kIHN1Z2dlc3Rpb25zIGlmIHlvdSB3YW50IHRvIGRlbHZlIGRlZXBlciBpbnRvIHRoZSB0b3BpYzoKCi0gSWYgeW91IHdhbnQgdG8gZGl2ZSBpbnRvIG1ha2luZyBjb21tb24gZ3JhcGhpY3MgYXMgcXVpY2tseSBhcyBwb3NzaWJsZSwgdGhlIFtSIEdyYXBoaWNzIENvb2tib29rXShodHRwczovL3ItZ3JhcGhpY3Mub3JnLykgYnkgV2luc3RvbiBDaGFuZy4gSXQgcHJvdmlkZXMgYSBzZXQgb2YgcmVjaXBlcyB0byBzb2x2ZSBjb21tb24gZ3JhcGhpY3MgcHJvYmxlbXMuCi0gVGhlIFtEYXRhIFZpc3VhbGl6YXRpb25dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZGF0YS12aXN1YWxpc2F0aW9uLmh0bWwpIGFuZCBbR3JhcGhpY3MgZm9yIGNvbW11bmljYXRpb25dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZ3JhcGhpY3MtZm9yLWNvbW11bmljYXRpb24uaHRtbCkgY2hhcHRlcnMgaW4gW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pLgotIElmIHlvdeKAmWQgbGlrZSB0byBmb2xsb3cgYSB3ZWJpbmFyLCB0cnkgW1Bsb3R0aW5nIEFueXRoaW5nIHdpdGggZ2dwbG90Ml0oaHR0cHM6Ly95b3V0dS5iZS9oMjlnMjF6MGE2OCkgYnkgVGhvbWFzIExpbiBQZWRlcnNlbi4KLSBUaGUgZ2dwbG90MiBjaGVhdHNoZWV0OiAhW2dncGxvdDIgY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cuYnVzaW5lc3Mtc2NpZW5jZS5pby9hc3NldHMvMjAyMS0wNy0yNy1nZ2ZvcmNlLWh1bGwtcGxvdHMvZ2dwbG90Ml9jaGVhdHNoZWV0LmpwZykKCgoKCg==