Adapted from Visualize Data with ggplot2 by Garrett
Grolemund at RStudio.
First step: Load the library ggplot2 or load the tidyverse.
library(tidyverse)
For this exercise we will start by using an “on-board” (ggplot2)
highway mileage data set. The data set is called mpg. To learn about
this data set use the help features of R.
?mpg
#Various ways to explore the data
mpg
str(mpg)
tibble [234 × 11] (S3: tbl_df/tbl/data.frame)
$ manufacturer: chr [1:234] "audi" "audi" "audi" "audi" ...
$ model : chr [1:234] "a4" "a4" "a4" "a4" ...
$ displ : num [1:234] 1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
$ year : int [1:234] 1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
$ cyl : int [1:234] 4 4 4 4 6 6 6 4 4 4 ...
$ trans : chr [1:234] "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
$ drv : chr [1:234] "f" "f" "f" "f" ...
$ cty : int [1:234] 18 21 20 21 16 18 18 18 16 20 ...
$ hwy : int [1:234] 29 29 31 30 26 26 27 26 25 28 ...
$ fl : chr [1:234] "p" "p" "p" "p" ...
$ class : chr [1:234] "compact" "compact" "compact" "compact" ...
view(mpg)
glimpse(mpg)
Rows: 234
Columns: 11
$ manufacturer <chr> "audi", "audi", "audi", "a…
$ model <chr> "a4", "a4", "a4", "a4", "a…
$ displ <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2…
$ year <int> 1999, 1999, 2008, 2008, 19…
$ cyl <int> 4, 4, 4, 4, 6, 6, 6, 4, 4,…
$ trans <chr> "auto(l5)", "manual(m5)", …
$ drv <chr> "f", "f", "f", "f", "f", "…
$ cty <int> 18, 21, 20, 21, 16, 18, 18…
$ hwy <int> 29, 29, 31, 30, 26, 26, 27…
$ fl <chr> "p", "p", "p", "p", "p", "…
$ class <chr> "compact", "compact", "com…
#What if we want to rename the data?
my_data=mpg
glimpse(my_data)
Rows: 234
Columns: 11
$ manufacturer <chr> "audi", "audi", "audi", "a…
$ model <chr> "a4", "a4", "a4", "a4", "a…
$ displ <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2…
$ year <int> 1999, 1999, 2008, 2008, 19…
$ cyl <int> 4, 4, 4, 4, 6, 6, 6, 4, 4,…
$ trans <chr> "auto(l5)", "manual(m5)", …
$ drv <chr> "f", "f", "f", "f", "f", "…
$ cty <int> 18, 21, 20, 21, 16, 18, 18…
$ hwy <int> 29, 29, 31, 30, 26, 26, 27…
$ fl <chr> "p", "p", "p", "p", "p", "…
$ class <chr> "compact", "compact", "com…
Now we are ready to build a graphic. We are going to explore the
relationship between engine size (displ) and mileage (hwy) in different
ways?
Before we get started, a quick look at the base function
?ggplot
The two most important arguments are the data argument and the
mapping argument. So based on this we can create a basic template for
generating a plot.
ggplot2 template
ggplot(data = —, mapping = aes(x = —, y = —)) +
geom_—-()`*
Lets make a simple graph
Pay strict attention to spelling, capitalization, and
parentheses!
ggplot(data = mpg, aes(x = displ, y = hwy)) +
geom_point()

?ggplot
ggplot(data = mpg) +
geom_point(aes(x = displ, y = hwy))

##my preference
p1=ggplot(data = mpg, aes(x = displ, y = hwy))
p1+ geom_point()

We can customize the aesthetics aes() in a wide variety
of ways. We can do this in the base function or in any of the layers
separately. For instance we might want to change one or more the
following aesthetics.
Aesthetics Color Size Line type Opacity Shape
aes(blues9)
Aesthetic mapping:
* `x` -> `blues9`
We can do this by adding arguments for each to our aesthetics Add
color, size, alpha, and
shape aesthetics to your graph.
p1 + geom_point(aes(color = class))

p1 + geom_point(aes(size = cyl))

p1 + geom_point(aes(shape = drv))

p1 + geom_point(aes(alpha = hwy))

p1 + geom_point(aes(color = class,
size = cyl,
shape = drv,
alpha = hwy))

Note You have to be careful about where you specify
the arguments.
p1 + geom_point(aes(size = cyl,color="blue"))

#vs
p1 + geom_point(aes(size = cyl),color="blue")

#mapping
p1 + geom_point(aes(size = cyl,color=cyl))

Discrete v Continuous data
Aesthetics arguments (color, size, shape, etc.) are affected by mode
and type of data
Is your data
Discrete (Categorical: ordinal, nominal) Continuous (Numeric)
p1 + geom_point(aes(color = cyl))

GEOM TYPES
geoms = geometric objects
Here are some of the more common options…

but there are many more that can be found here (https://ggplot2.tidyverse.org/reference/index.html#section-layer-geoms)
Lets add a smoothed (loess fit) curve to our plot from above.
p1+geom_point()+geom_smooth()

Now lets change the mapping and make a stipplot, a boxplot, and a
violin plot of the variable hwy as a function of class.
p2=ggplot(data = mpg, aes(x = class, y = hwy))
p2+geom_point() #strip chart

##note that you can override the mappings in the base command
p1+geom_point(aes(class, hwy))

##
p2+geom_boxplot(aes(class, hwy))

p2+geom_violin(aes(class, hwy))

One last geom…make a histogram of hwy .
p2=ggplot(data = mpg, aes(x = class, y = hwy))
p2 <- ggplot(data = mpg, aes(hwy))+geom_histogram()
p2

geom_histogram(aes(hwy))
mapping: x = ~hwy
geom_bar: na.rm = FALSE, orientation = NA
stat_bin: binwidth = NULL, bins = NULL, na.rm = FALSE, orientation = NA, pad = FALSE
position_stack
stat_bin(30)
Error in `validate_mapping()`:
! `mapping` must be created by `aes()`
Backtrace:
1. ggplot2::stat_bin(30)
2. ggplot2::layer(...)
3. ggplot2:::validate_mapping(mapping)
Challenge
Make a density plot of hwy colored by
class.
p2=ggplot(data = mpg, aes(x = class, y = hwy))
data <- hwy
Error: object 'hwy' not found
Challenge
Make a bar chart hwy colored by class.
Faceting Faceting is an effective way to summarize
data, visualize interactions or display patterns when there are multiple
processes happening.
use facet_grid and facet_wrap
p1+geom_point()

Now lets create facets based on the category class
p1+geom_point()+facet_wrap(~ class)

You can do this with as much aes mapping as you want
p1+geom_point(aes(color = class,size = cyl,shape = drv, alpha = hwy))+facet_wrap(~class)

You can also facet by multiple factors,
p1+geom_point(aes(color = class,alpha = hwy))+facet_grid(cyl~drv)

NA
NA
Beautifying/customizing your plot
Changing axis labels You can use labs() to name
all axes and add titles xlab() or ylab() to
just rename 1 axes ggtitle() to just add a title
p1+geom_point()

p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")

p1+geom_point()+labs(x = "Displacement", y = "Highway mpg",title = "Automobile mileage", caption = "Source: ggplot2::mpg")

Changing axis limits ylim()
xlim()
p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+ylim(0,100)

Changing the text size and other aspects of appearance
Themes
Several simple built in theme functions are available in ggplot2 (https://ggplot2.tidyverse.org/reference/ggtheme.html).
These include:
theme_gray(): Gray background color and white grid
lines. It puts the data forward to make comparisons easy.
theme_bw(): White background and gray grid lines. May
work best for presentations displayed with a projector.
theme_linedraw(): A theme with black lines of various
widths on white backgrounds, reminiscent of a line drawings.
theme_light(): A theme similar to theme_linedraw() but
with light grey lines and axes, to direct more attention towards the
data.
theme_dark(): The dark cousin of theme_light(), with
similar line sizes but a dark background. Useful to make thin coloured
lines pop out.
theme_tufte: Theme based on Chapter 6 ‘Data-Ink
Maximization and Graphical Design’ of Edward Tufte The Visual
Display of Quantitative Information.
Lets try a few:
p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_gray()

p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_bw()

p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_linedraw()

p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_light()

p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_dark()

library(ggthemes)
p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_tufte()

You can also modify specific aspects of the theme
Changing text size: Use theme
theme(axis.text.x = element_text(size=15,colour = "black"), axis.title.x = element_text(size=15,face="bold"))
theme(axis.text.y = element_text(size=15,colour = "black"), axis.title.y = element_text(size=15,face="bold"))
theme(plot.background = element_blank(),panel.grid.major = element_blank(), panel.grid.minor = element_blank())
For example: To increase text size on x axis:
p6 <-p1+geom_point()+labs(x = "Displacement", y = "Highway mpg")+theme_bw()
p6

p6+theme(axis.text.x = element_text(size=15,colour = "black", hjust=1),
axis.title.x = element_text(size=15,face="bold"))

increase y axis:
p6+theme(axis.text.y = element_text(size=15,colour = "black"),
axis.title.y = element_text(size=15,face="bold"))

Get rid of grid lines:
p6+theme(plot.background = element_blank(),panel.grid.major = element_blank(),
panel.grid.minor = element_blank())

put them all together
p6+
theme(axis.text.x = element_text(size=15,colour = "black", hjust=1),
axis.title.x = element_text(size=15,face="bold")) +
theme(axis.text.y = element_text(size=15,colour = "black"),
axis.title.y = element_text(size=15,face="bold")) +
theme(plot.background = element_blank(),panel.grid.major = element_blank(),
panel.grid.minor = element_blank())

You can also change the text in the facets
p1+geom_point(aes(color = class,size = cyl,shape = drv, alpha = hwy))+facet_wrap(~drv)+ theme(strip.text.x = element_text(size = 15,face="bold"),strip.text.y = element_text(size = 15,face="bold"))

NA
##Statistical summaries
Load in the “Culcita” data.
Culcita - Another new type of data
load("culcita.RData")
summary(culcita_dat)
block predation ttt
1 : 8 Min. :0.000 none :20
2 : 8 1st Qu.:0.000 crabs :20
3 : 8 Median :1.000 shrimp:20
4 : 8 Mean :0.625 both :20
5 : 8 3rd Qu.:1.000
6 : 8 Max. :1.000
(Other):32
These data are from McKeon et al. 2012 “Multiple defender
effects: synergistic coral defense by mutualist crustaceans”
Oecologia, 169(4):1095-1103.
The basic data can be reduced, for the purposes of this exercise, to
a single treatment [which consists of combinations of different
symbionts: [crab, shrimp, both or neither]; a binary response
(predation). We want to generate a plot displaying summary
statistics from this experiment.
plot1<- ggplot(culcita_dat,aes(x=ttt,y=predation))
plot1+geom_point()

plot1+geom_point()+geom_violin()

Summary functions You can either supply summary
functions individually or as a single function (fun.data):
fun.data Complete summary function. Should take numeric
vector as input and return data frame as output
fun.min min summary function (should take numeric vector and
return single number)
fun main summary function (should take numeric vector and
return single number)
fun.max max summary function (should take numeric vector and
return single number)
ggplot(culcita_dat,aes(x=ttt,y=predation))+
stat_summary(fun=mean,size=2)+
ylim(c(0,1))+xlab("Treatment")

ggplot(culcita_dat,aes(x=ttt,y=predation))+
stat_summary(fun=mean,, fun.min = min, fun.max = max, size=2)+
ylim(c(0,1))+xlab("Treatment")

ggplot(culcita_dat,aes(x=ttt,y=predation))+
stat_summary(fun=median,size=2)+
ylim(c(0,1))+xlab("Treatment")

ggplot(culcita_dat,aes(x=ttt,y=predation))+
stat_summary(fun.data=mean_se,size=2)+
ylim(c(0,1))+xlab("Treatment")

ggplot(culcita_dat,aes(x=ttt,y=predation))+
stat_summary(fun.data=mean_cl_boot,size=2)+
ylim(c(0,1))+xlab("Treatment")

Challenges
The data are from L. Partridge and M. Farquhar (1981), Sexual
activity and the lifespan of male fruitflies, Nature 294: 580-581. The
experiment placed male fruit flies with varying numbers of
previously-mated or virgin females to investigate how mating activity
affects male lifespan.
Read the data file (fruitflies.csv) into your R environment and name
it.
1. Create a boxplot displaying Longevity for each
“treatment”.
Be sure to label the axes and make sure the text is readable.
2. The variable thorax stands for thorax length, which was used
as a measure of body size. The measurement was included in case body
size also affected longevity. Produce a scatter plot of thorax length
and longevity. Make longevity the response variable (i.e., plot it on
the vertical axis). Make the symbols and colors differ among
treatments
3. Redraw the scatter plot above except create separate facets
for each treatment.
Final Challenge
Load the data “WRC_Plant Diversity.csv”
These data are from Long‐term nutrient enrichment, mowing, and
ditch drainage interact in the dynamics of a wetland plant
community by Goodwillie et al. 2020 Ecosphere. The experimental
design was a 2x2 factorial experiment with 2 levels of disturbance
(mowed or unmowed) and 2 levels of nutrient addition (fertilized and
unfertilized). The experiment was arranged in 8 spatial blocks with each
containing the 4 treatment plots. Three fixed quadrats were created
within each plot that were sampled annually. The spatial blocks were
arranged in two rows that differed in proximity to a ditch. Your
challenge is to draw a figure that shows the mean and confidence
intervals for alpha diversity over time (Year) in a way that highlights
the independent effects of Mowing, Fertilizer treatment and proximity to
the ditch.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpBZGFwdGVkIGZyb20gKlZpc3VhbGl6ZSBEYXRhIHdpdGggZ2dwbG90MiogYnkgR2FycmV0dCBHcm9sZW11bmQgYXQgUlN0dWRpby4NCg0KDQpGaXJzdCBzdGVwOiBMb2FkIHRoZSBsaWJyYXJ5IGdncGxvdDIgb3IgbG9hZCB0aGUgdGlkeXZlcnNlLiANCg0KYGBge3Igc2V0dXB9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpGb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIHN0YXJ0IGJ5IHVzaW5nIGFuICJvbi1ib2FyZCIgKGdncGxvdDIpIGhpZ2h3YXkgbWlsZWFnZSBkYXRhIHNldC4gVGhlIGRhdGEgc2V0IGlzIGNhbGxlZCBtcGcuIFRvIGxlYXJuIGFib3V0IHRoaXMgZGF0YSBzZXQgdXNlIHRoZSBoZWxwIGZlYXR1cmVzIG9mIFIuDQoNCmBgYHtyfQ0KP21wZw0KDQpgYGANCg0KYGBge3J9DQojVmFyaW91cyB3YXlzIHRvIGV4cGxvcmUgdGhlIGRhdGENCm1wZw0Kc3RyKG1wZykNCnZpZXcobXBnKQ0KZ2xpbXBzZShtcGcpDQoNCiNXaGF0IGlmIHdlIHdhbnQgdG8gcmVuYW1lIHRoZSBkYXRhPw0KbXlfZGF0YT1tcGcNCmdsaW1wc2UobXlfZGF0YSkNCmBgYA0KTm93IHdlIGFyZSByZWFkeSB0byBidWlsZCBhIGdyYXBoaWMuIFdlIGFyZSBnb2luZyB0byBleHBsb3JlICB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZW5naW5lIHNpemUgKGRpc3BsKSBhbmQgbWlsZWFnZSAoaHd5KSBpbiBkaWZmZXJlbnQgd2F5cz8NCg0KQmVmb3JlIHdlIGdldCBzdGFydGVkLCBhIHF1aWNrIGxvb2sgYXQgdGhlIGJhc2UgZnVuY3Rpb24gIA0KYGBge3J9DQo/Z2dwbG90DQpgYGANCg0KVGhlIHR3byBtb3N0IGltcG9ydGFudCBhcmd1bWVudHMgYXJlIHRoZSBkYXRhIGFyZ3VtZW50IGFuZCB0aGUgbWFwcGluZyBhcmd1bWVudC4gU28gYmFzZWQgb24gdGhpcyB3ZSBjYW4gY3JlYXRlIGEgYmFzaWMgdGVtcGxhdGUgZm9yIGdlbmVyYXRpbmcgYSBwbG90Lg0KDQoqKmdncGxvdDIgdGVtcGxhdGUqKg0KDQoqZ2dwbG90KGRhdGEgPSAtLS0sIG1hcHBpbmcgPSBhZXMoeCA9IC0tLSwgeSA9IC0tLSkpICsgZ2VvbV8tLS0tKCkqYCoNCiAgDQoqKkxldHMgbWFrZSBhIHNpbXBsZSBncmFwaCoqDQoNClBheSBzdHJpY3QgYXR0ZW50aW9uIHRvIHNwZWxsaW5nLCBjYXBpdGFsaXphdGlvbiwgYW5kIHBhcmVudGhlc2VzIQ0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKw0KICBnZW9tX3BvaW50KCkNCg0KP2dncGxvdA0KDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQ0KDQoNCiMjbXkgcHJlZmVyZW5jZQ0KDQpwMT1nZ3Bsb3QoZGF0YSA9IG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpDQpwMSsgIGdlb21fcG9pbnQoKQ0KDQpgYGANCldlIGNhbiBjdXN0b21pemUgdGhlIGFlc3RoZXRpY3MgYGFlcygpYCBpbiBhIHdpZGUgdmFyaWV0eSBvZiB3YXlzLiBXZSBjYW4gZG8gdGhpcyBpbiB0aGUgYmFzZSBmdW5jdGlvbiBvciBpbiBhbnkgb2YgdGhlIGxheWVycyBzZXBhcmF0ZWx5LiAgRm9yIGluc3RhbmNlIHdlIG1pZ2h0IHdhbnQgdG8gY2hhbmdlIG9uZSBvciBtb3JlIHRoZSBmb2xsb3dpbmcgYWVzdGhldGljcy4NCg0KKipBZXN0aGV0aWNzKioNCkNvbG9yDQpTaXplDQpMaW5lIHR5cGUNCk9wYWNpdHkNClNoYXBlDQpgYGB7cn0NCmFlcyhibHVlczkpDQpgYGANCg0KV2UgY2FuIGRvIHRoaXMgYnkgYWRkaW5nIGFyZ3VtZW50cyBmb3IgZWFjaCB0byBvdXIgYWVzdGhldGljcw0KQWRkIGBjb2xvcmAsIGBzaXplYCwgYGFscGhhYCwgYW5kIGBzaGFwZWAgYWVzdGhldGljcyB0byB5b3VyIGdyYXBoLiAgDQoNCmBgYHtyfQ0KcDEgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkNCg0KcDEgKyBnZW9tX3BvaW50KGFlcyhzaXplID0gY3lsKSkNCg0KcDEgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGRydikpDQoNCnAxICsgZ2VvbV9wb2ludChhZXMoYWxwaGEgPSBod3kpKQ0KDQpwMSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gY3lsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBkcnYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IGh3eSkpDQpgYGANCioqTm90ZSoqDQpZb3UgaGF2ZSB0byBiZSBjYXJlZnVsIGFib3V0IHdoZXJlIHlvdSBzcGVjaWZ5IHRoZSBhcmd1bWVudHMuIA0KDQpgYGB7cn0NCnAxICsgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGN5bCxjb2xvcj0iYmx1ZSIpKQ0KI3ZzDQpwMSArIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjeWwpLGNvbG9yPSJibHVlIikNCiNtYXBwaW5nDQpwMSArIGdlb21fcG9pbnQoYWVzKHNpemUgPSBjeWwsY29sb3I9Y3lsKSkNCmBgYA0KKipEaXNjcmV0ZSB2IENvbnRpbnVvdXMgZGF0YSoqDQoNCkFlc3RoZXRpY3MgYXJndW1lbnRzIChjb2xvciwgc2l6ZSwgc2hhcGUsIGV0Yy4pIGFyZSBhZmZlY3RlZCBieSBtb2RlIGFuZCB0eXBlIG9mIGRhdGENCg0KSXMgeW91ciBkYXRhDQoNCkRpc2NyZXRlIChDYXRlZ29yaWNhbDogb3JkaW5hbCwgbm9taW5hbCkgQ29udGludW91cyAoTnVtZXJpYykNCg0KYGBge3J9DQpwMSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY3lsKSkNCmBgYA0KDQoqKkdFT00gVFlQRVMqKg0KDQpnZW9tcyA9IGdlb21ldHJpYyBvYmplY3RzDQoNCkhlcmUgYXJlIHNvbWUgb2YgdGhlIG1vcmUgY29tbW9uIG9wdGlvbnMuLi4NCg0KIVtdKGltYWdlcy9TY3JlZW4gU2hvdCAyMDIyLTA1LTI3IGF0IDIuMjIuMDQgUE0ucG5nKQ0KDQpidXQgdGhlcmUgYXJlIG1hbnkgbW9yZSB0aGF0IGNhbiBiZSBmb3VuZCBoZXJlIChodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCNzZWN0aW9uLWxheWVyLWdlb21zKQ0KDQpMZXRzIGFkZCBhIHNtb290aGVkIChsb2VzcyBmaXQpIGN1cnZlIHRvIG91ciBwbG90IGZyb20gYWJvdmUuDQpgYGB7cn0NCnAxK2dlb21fcG9pbnQoKStnZW9tX3Ntb290aCgpDQpgYGANCg0KTm93IGxldHMgY2hhbmdlIHRoZSBtYXBwaW5nIGFuZCBtYWtlIGEgc3RpcHBsb3QsIGEgYm94cGxvdCwgYW5kIGEgdmlvbGluIHBsb3Qgb2YgdGhlIHZhcmlhYmxlIGh3eSBhcyBhIGZ1bmN0aW9uIG9mIGNsYXNzLg0KDQpgYGB7cn0NCnAyPWdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkNCnAyK2dlb21fcG9pbnQoKSAjc3RyaXAgY2hhcnQNCg0KIyNub3RlIHRoYXQgeW91IGNhbiBvdmVycmlkZSB0aGUgbWFwcGluZ3MgaW4gdGhlIGJhc2UgY29tbWFuZA0KcDErZ2VvbV9wb2ludChhZXMoY2xhc3MsIGh3eSkpDQojIw0KDQpwMitnZW9tX2JveHBsb3QoYWVzKGNsYXNzLCBod3kpKQ0KcDIrZ2VvbV92aW9saW4oYWVzKGNsYXNzLCBod3kpKQ0KYGBgDQoNCk9uZSBsYXN0IGdlb20uLi5tYWtlIGEgaGlzdG9ncmFtIG9mICBgaHd5YCAuDQoNCmBgYHtyfQ0KcDI9Z2dwbG90KGRhdGEgPSBtcGcsIGFlcyh4ID0gY2xhc3MsIHkgPSBod3kpKQ0KcDIgPC0gZ2dwbG90KGRhdGEgPSBtcGcsIGFlcyhod3kpKStnZW9tX2hpc3RvZ3JhbSgpDQpwMg0KZ2VvbV9oaXN0b2dyYW0oYWVzKGh3eSkpDQpzdGF0X2JpbigzMCkNCmBgYA0KDQoqKkNoYWxsZW5nZSoqDQoNCk1ha2UgYSBkZW5zaXR5IHBsb3Qgb2YgYGh3eWAgY29sb3JlZCBieSBgY2xhc3NgLg0KDQpgYGB7cn0NCnAyPWdncGxvdChkYXRhID0gbXBnLCBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkNCmRhdGEgPC0gaHd5DQpwbG90KGRlbnNpdHkoaHd5JGNsYXNzKSkNCg0KYGBgDQoNCioqQ2hhbGxlbmdlKioNCg0KTWFrZSBhIGJhciBjaGFydCBgaHd5YCBjb2xvcmVkIGJ5IGBjbGFzc2AuDQoNCmBgYHtyfQ0KDQoNCmBgYA0KDQoqKkZhY2V0aW5nKioNCkZhY2V0aW5nIGlzIGFuIGVmZmVjdGl2ZSB3YXkgdG8gc3VtbWFyaXplIGRhdGEsIHZpc3VhbGl6ZSBpbnRlcmFjdGlvbnMgb3IgZGlzcGxheSBwYXR0ZXJucyB3aGVuIHRoZXJlIGFyZSBtdWx0aXBsZSBwcm9jZXNzZXMgaGFwcGVuaW5nLiAgDQp1c2UgYGZhY2V0X2dyaWRgIGFuZCBgZmFjZXRfd3JhcGANCg0KYGBge3J9DQpwMStnZW9tX3BvaW50KCkgDQoNCmBgYA0KTm93IGxldHMgY3JlYXRlIGZhY2V0cyBiYXNlZCBvbiB0aGUgY2F0ZWdvcnkgY2xhc3MNCmBgYHtyfQ0KIHAxK2dlb21fcG9pbnQoKStmYWNldF93cmFwKH4gY2xhc3MpDQpgYGANCg0KWW91IGNhbiBkbyB0aGlzIHdpdGggYXMgbXVjaCBhZXMgbWFwcGluZyBhcyB5b3Ugd2FudA0KYGBge3J9DQpwMStnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzLHNpemUgPSBjeWwsc2hhcGUgPSBkcnYsIGFscGhhID0gaHd5KSkrZmFjZXRfd3JhcCh+Y2xhc3MpDQpgYGANCg0KDQpZb3UgY2FuIGFsc28gZmFjZXQgYnkgbXVsdGlwbGUgZmFjdG9ycywgDQoNCmBgYHtyfQ0KcDErZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcyxhbHBoYSA9IGh3eSkpK2ZhY2V0X2dyaWQoY3lsfmRydikNCg0KDQpgYGANCg0KKipCZWF1dGlmeWluZy9jdXN0b21pemluZyB5b3VyIHBsb3QqKg0KDQoqQ2hhbmdpbmcgYXhpcyBsYWJlbHMqDQpZb3UgY2FuIHVzZSANCmBsYWJzKClgIHRvIG5hbWUgYWxsIGF4ZXMgYW5kIGFkZCB0aXRsZXMNCmB4bGFiKClgIG9yIGB5bGFiKClgIHRvIGp1c3QgcmVuYW1lIDEgYXhlcw0KYGdndGl0bGUoKWAgdG8ganVzdCBhZGQgYSB0aXRsZQ0KDQpgYGB7cn0NCnAxK2dlb21fcG9pbnQoKQ0KDQpwMStnZW9tX3BvaW50KCkrbGFicyh4ID0gIkRpc3BsYWNlbWVudCIsIHkgPSAiSGlnaHdheSBtcGciKQ0KDQpwMStnZW9tX3BvaW50KCkrbGFicyh4ID0gIkRpc3BsYWNlbWVudCIsIHkgPSAiSGlnaHdheSBtcGciLHRpdGxlID0gIkF1dG9tb2JpbGUgbWlsZWFnZSIsIGNhcHRpb24gPSAiU291cmNlOiAgZ2dwbG90Mjo6bXBnIikNCg0KYGBgDQoNCipDaGFuZ2luZyBheGlzIGxpbWl0cyoNCmB5bGltKClgDQpgeGxpbSgpYA0KDQoNCmBgYHtyfQ0KcDErZ2VvbV9wb2ludCgpK2xhYnMoeCA9ICJEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgbXBnIikreWxpbSgwLDEwMCkNCg0KYGBgDQoNCipDaGFuZ2luZyB0aGUgdGV4dCBzaXplIGFuZCBvdGhlciBhc3BlY3RzIG9mIGFwcGVhcmFuY2UqDQoNCioqVGhlbWVzKioNCg0KU2V2ZXJhbCBzaW1wbGUgYnVpbHQgaW4gdGhlbWUgZnVuY3Rpb25zIGFyZSBhdmFpbGFibGUgaW4gZ2dwbG90MiAoaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dndGhlbWUuaHRtbCkuIFRoZXNlIGluY2x1ZGU6DQoNCmB0aGVtZV9ncmF5KClgOiBHcmF5IGJhY2tncm91bmQgY29sb3IgYW5kIHdoaXRlIGdyaWQgbGluZXMuIEl0IHB1dHMgdGhlIGRhdGEgZm9yd2FyZCB0byBtYWtlIGNvbXBhcmlzb25zIGVhc3kuDQoNCmB0aGVtZV9idygpYDogV2hpdGUgYmFja2dyb3VuZCBhbmQgZ3JheSBncmlkIGxpbmVzLiBNYXkgd29yayBiZXN0IGZvciBwcmVzZW50YXRpb25zIGRpc3BsYXllZCB3aXRoIGEgcHJvamVjdG9yLg0KDQpgdGhlbWVfbGluZWRyYXcoKWA6IEEgdGhlbWUgd2l0aCBibGFjayBsaW5lcyBvZiB2YXJpb3VzIHdpZHRocyBvbiB3aGl0ZSBiYWNrZ3JvdW5kcywgcmVtaW5pc2NlbnQgb2YgYSBsaW5lIGRyYXdpbmdzLg0KDQpgdGhlbWVfbGlnaHQoKWA6IEEgdGhlbWUgc2ltaWxhciB0byB0aGVtZV9saW5lZHJhdygpIGJ1dCB3aXRoIGxpZ2h0IGdyZXkgbGluZXMgYW5kIGF4ZXMsIHRvIGRpcmVjdCBtb3JlIGF0dGVudGlvbiB0b3dhcmRzIHRoZSBkYXRhLg0KDQpgdGhlbWVfZGFyaygpYDogVGhlIGRhcmsgY291c2luIG9mIHRoZW1lX2xpZ2h0KCksIHdpdGggc2ltaWxhciBsaW5lIHNpemVzIGJ1dCBhIGRhcmsgYmFja2dyb3VuZC4gVXNlZnVsIHRvIG1ha2UgdGhpbiBjb2xvdXJlZCBsaW5lcyBwb3Agb3V0Lg0KDQpgdGhlbWVfdHVmdGVgOiBUaGVtZSBiYXNlZCBvbiBDaGFwdGVyIDYgJ0RhdGEtSW5rIE1heGltaXphdGlvbiBhbmQgR3JhcGhpY2FsIERlc2lnbicgb2YgRWR3YXJkIFR1ZnRlICpUaGUgVmlzdWFsIERpc3BsYXkgb2YgUXVhbnRpdGF0aXZlIEluZm9ybWF0aW9uKi4NCg0KTGV0cyB0cnkgYSBmZXc6DQpgYGB7cn0NCnAxK2dlb21fcG9pbnQoKStsYWJzKHggPSAiRGlzcGxhY2VtZW50IiwgeSA9ICJIaWdod2F5IG1wZyIpK3RoZW1lX2dyYXkoKQ0KcDErZ2VvbV9wb2ludCgpK2xhYnMoeCA9ICJEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgbXBnIikrdGhlbWVfYncoKQ0KcDErZ2VvbV9wb2ludCgpK2xhYnMoeCA9ICJEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgbXBnIikrdGhlbWVfbGluZWRyYXcoKQ0KcDErZ2VvbV9wb2ludCgpK2xhYnMoeCA9ICJEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgbXBnIikrdGhlbWVfbGlnaHQoKQ0KcDErZ2VvbV9wb2ludCgpK2xhYnMoeCA9ICJEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgbXBnIikrdGhlbWVfZGFyaygpDQpsaWJyYXJ5KGdndGhlbWVzKQ0KcDErZ2VvbV9wb2ludCgpK2xhYnMoeCA9ICJEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgbXBnIikrdGhlbWVfdHVmdGUoKQ0KYGBgDQoqWW91IGNhbiBhbHNvIG1vZGlmeSBzcGVjaWZpYyBhc3BlY3RzIG9mIHRoZSB0aGVtZSoNCg0KQ2hhbmdpbmcgdGV4dCBzaXplOiBVc2UgdGhlbWUNCg0KDQpgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNSxjb2xvdXIgPSAiYmxhY2siKSwNCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE1LGZhY2U9ImJvbGQiKSlgIA0KDQoNCmB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE1LGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTUsZmFjZT0iYm9sZCIpKWAgDQoNCmB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkscGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpYA0KDQpGb3IgZXhhbXBsZTogDQpUbyBpbmNyZWFzZSB0ZXh0IHNpemUgb24geCBheGlzOg0KYGBge3J9DQpwNiA8LXAxK2dlb21fcG9pbnQoKStsYWJzKHggPSAiRGlzcGxhY2VtZW50IiwgeSA9ICJIaWdod2F5IG1wZyIpK3RoZW1lX2J3KCkNCnA2DQoNCnA2K3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTUsY29sb3VyID0gImJsYWNrIiwgaGp1c3Q9MSksDQogICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNSxmYWNlPSJib2xkIikpIA0KYGBgDQoNCmluY3JlYXNlIHkgYXhpczoNCg0KYGBge3J9DQoNCnA2K3RoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTUsY29sb3VyID0gImJsYWNrIiksDQogICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xNSxmYWNlPSJib2xkIikpIA0KDQpgYGANCg0KR2V0IHJpZCBvZiBncmlkIGxpbmVzOg0KDQpgYGB7cn0NCnA2K3RoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpwdXQgdGhlbSBhbGwgdG9nZXRoZXINCg0KYGBge3J9DQpwNisNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNSxjb2xvdXIgPSAiYmxhY2siLCBoanVzdD0xKSwNCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE1LGZhY2U9ImJvbGQiKSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE1LGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTUsZmFjZT0iYm9sZCIpKSArDQogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpZb3UgY2FuIGFsc28gY2hhbmdlIHRoZSB0ZXh0IGluIHRoZSBmYWNldHMNCg0KYGBge3J9DQpwMStnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzLHNpemUgPSBjeWwsc2hhcGUgPSBkcnYsIGFscGhhID0gaHd5KSkrZmFjZXRfd3JhcCh+ZHJ2KSsgdGhlbWUoc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSxmYWNlPSJib2xkIiksc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSxmYWNlPSJib2xkIikpDQogIA0KYGBgDQoNCiMjU3RhdGlzdGljYWwgc3VtbWFyaWVzDQoNCkxvYWQgaW4gdGhlICJDdWxjaXRhIiBkYXRhLiAgDQoNCiMjICpDdWxjaXRhKiAtIEFub3RoZXIgbmV3IHR5cGUgb2YgZGF0YQ0KDQpgYGB7cn0NCmxvYWQoImN1bGNpdGEuUkRhdGEiKQ0Kc3VtbWFyeShjdWxjaXRhX2RhdCkNCmBgYA0KDQoNClRoZXNlIGRhdGEgYXJlIGZyb20gTWNLZW9uICpldCBhbCouIDIwMTIgIk11bHRpcGxlIGRlZmVuZGVyIGVmZmVjdHM6IHN5bmVyZ2lzdGljIGNvcmFsIGRlZmVuc2UgYnkgbXV0dWFsaXN0IGNydXN0YWNlYW5zIiAqT2Vjb2xvZ2lhKiwgMTY5KDQpOjEwOTUtMTEwMy4gDQoNClRoZSBiYXNpYyBkYXRhIGNhbiBiZSByZWR1Y2VkLCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgZXhlcmNpc2UsIHRvIGEgc2luZ2xlIHRyZWF0bWVudCBbd2hpY2ggY29uc2lzdHMgb2YgY29tYmluYXRpb25zIG9mIGRpZmZlcmVudCBzeW1iaW9udHM6IFtjcmFiLCBzaHJpbXAsIGJvdGggb3IgbmVpdGhlcl07IGEgYmluYXJ5IHJlc3BvbnNlIChgcHJlZGF0aW9uYCkuICBXZSB3YW50IHRvIGdlbmVyYXRlIGEgcGxvdCBkaXNwbGF5aW5nIHN1bW1hcnkgc3RhdGlzdGljcyBmcm9tIHRoaXMgZXhwZXJpbWVudC4NCg0KYGBge3J9DQpwbG90MTwtIGdncGxvdChjdWxjaXRhX2RhdCxhZXMoeD10dHQseT1wcmVkYXRpb24pKQ0KcGxvdDErZ2VvbV9wb2ludCgpDQpwbG90MStnZW9tX3BvaW50KCkrZ2VvbV92aW9saW4oKQ0KYGBgDQoNCioqU3VtbWFyeSBmdW5jdGlvbnMqKg0KWW91IGNhbiBlaXRoZXIgc3VwcGx5IHN1bW1hcnkgZnVuY3Rpb25zIGluZGl2aWR1YWxseSBvciBhcyBhIHNpbmdsZSBmdW5jdGlvbiAoZnVuLmRhdGEpOg0KDQoqZnVuLmRhdGEqDQpDb21wbGV0ZSBzdW1tYXJ5IGZ1bmN0aW9uLiBTaG91bGQgdGFrZSBudW1lcmljIHZlY3RvciBhcyBpbnB1dCBhbmQgcmV0dXJuIGRhdGEgZnJhbWUgYXMgb3V0cHV0DQoNCipmdW4ubWluKg0KbWluIHN1bW1hcnkgZnVuY3Rpb24gKHNob3VsZCB0YWtlIG51bWVyaWMgdmVjdG9yIGFuZCByZXR1cm4gc2luZ2xlIG51bWJlcikNCg0KKmZ1bioNCm1haW4gc3VtbWFyeSBmdW5jdGlvbiAoc2hvdWxkIHRha2UgbnVtZXJpYyB2ZWN0b3IgYW5kIHJldHVybiBzaW5nbGUgbnVtYmVyKQ0KDQoqZnVuLm1heCoNCm1heCBzdW1tYXJ5IGZ1bmN0aW9uIChzaG91bGQgdGFrZSBudW1lcmljIHZlY3RvciBhbmQgcmV0dXJuIHNpbmdsZSBudW1iZXIpDQoNCmBgYHtyfQ0KZ2dwbG90KGN1bGNpdGFfZGF0LGFlcyh4PXR0dCx5PXByZWRhdGlvbikpKw0KICBzdGF0X3N1bW1hcnkoZnVuPW1lYW4sc2l6ZT0yKSsNCiAgeWxpbShjKDAsMSkpK3hsYWIoIlRyZWF0bWVudCIpDQoNCmdncGxvdChjdWxjaXRhX2RhdCxhZXMoeD10dHQseT1wcmVkYXRpb24pKSsNCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCwgZnVuLm1pbiA9IG1pbiwgZnVuLm1heCA9IG1heCwgc2l6ZT0yKSsNCiAgeWxpbShjKDAsMSkpK3hsYWIoIlRyZWF0bWVudCIpDQoNCmdncGxvdChjdWxjaXRhX2RhdCxhZXMoeD10dHQseT1wcmVkYXRpb24pKSsNCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWRpYW4sc2l6ZT0yKSsNCiAgeWxpbShjKDAsMSkpK3hsYWIoIlRyZWF0bWVudCIpDQoNCmdncGxvdChjdWxjaXRhX2RhdCxhZXMoeD10dHQseT1wcmVkYXRpb24pKSsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhPW1lYW5fc2Usc2l6ZT0yKSsNCiAgeWxpbShjKDAsMSkpK3hsYWIoIlRyZWF0bWVudCIpDQoNCmdncGxvdChjdWxjaXRhX2RhdCxhZXMoeD10dHQseT1wcmVkYXRpb24pKSsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhPW1lYW5fY2xfYm9vdCxzaXplPTIpKw0KICB5bGltKGMoMCwxKSkreGxhYigiVHJlYXRtZW50IikNCg0KYGBgDQoNCg0KDQpDaGFsbGVuZ2VzDQoNClRoZSBkYXRhIGFyZSBmcm9tIEwuIFBhcnRyaWRnZSBhbmQgTS4gRmFycXVoYXIgKDE5ODEpLCBTZXh1YWwgYWN0aXZpdHkgYW5kIHRoZSBsaWZlc3BhbiBvZiBtYWxlIGZydWl0ZmxpZXMsIE5hdHVyZSAyOTQ6IDU4MC01ODEuIFRoZSBleHBlcmltZW50IHBsYWNlZCBtYWxlIGZydWl0IGZsaWVzIHdpdGggdmFyeWluZyBudW1iZXJzIG9mIHByZXZpb3VzbHktbWF0ZWQgb3IgdmlyZ2luIGZlbWFsZXMgdG8gaW52ZXN0aWdhdGUgaG93IG1hdGluZyBhY3Rpdml0eSBhZmZlY3RzIG1hbGUgbGlmZXNwYW4uIA0KDQoNClJlYWQgdGhlIGRhdGEgZmlsZSAoZnJ1aXRmbGllcy5jc3YpIGludG8geW91ciBSIGVudmlyb25tZW50IGFuZCBuYW1lIGl0Lg0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KDQoqMS4gQ3JlYXRlIGEgYm94cGxvdCBkaXNwbGF5aW5nIExvbmdldml0eSBmb3IgZWFjaCAidHJlYXRtZW50Ii4qICANCg0KQmUgc3VyZSB0byBsYWJlbCB0aGUgYXhlcyBhbmQgbWFrZSBzdXJlIHRoZSB0ZXh0IGlzIHJlYWRhYmxlLg0KYGBge3J9DQojIEJveCBwbG90IHVzaW5nIGdncGxvdA0KDQpgYGANCg0KKjIuIFRoZSB2YXJpYWJsZSB0aG9yYXggc3RhbmRzIGZvciB0aG9yYXggbGVuZ3RoLCB3aGljaCB3YXMgdXNlZCBhcyBhIG1lYXN1cmUgb2YgYm9keSBzaXplLiBUaGUgbWVhc3VyZW1lbnQgd2FzIGluY2x1ZGVkIGluIGNhc2UgYm9keSBzaXplIGFsc28gYWZmZWN0ZWQgbG9uZ2V2aXR5LiBQcm9kdWNlIGEgc2NhdHRlciBwbG90IG9mIHRob3JheCBsZW5ndGggYW5kIGxvbmdldml0eS4gTWFrZSBsb25nZXZpdHkgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIChpLmUuLCBwbG90IGl0IG9uIHRoZSB2ZXJ0aWNhbCBheGlzKS4gTWFrZSB0aGUgc3ltYm9scyBhbmQgY29sb3JzIGRpZmZlciBhbW9uZyB0cmVhdG1lbnRzKg0KDQpgYGB7cn0NCg0KDQpgYGANCg0KKjMuIFJlZHJhdyB0aGUgc2NhdHRlciBwbG90IGFib3ZlIGV4Y2VwdCBjcmVhdGUgc2VwYXJhdGUgZmFjZXRzIGZvciBlYWNoIHRyZWF0bWVudC4qDQoNCmBgYHtyfQ0KDQoNCmBgYA0KDQoNCioqRmluYWwgQ2hhbGxlbmdlKioNCg0KTG9hZCB0aGUgZGF0YSAiV1JDX1BsYW50IERpdmVyc2l0eS5jc3YiDQoNClRoZXNlIGRhdGEgYXJlIGZyb20gKkxvbmfigJB0ZXJtIG51dHJpZW50IGVucmljaG1lbnQsIG1vd2luZywgYW5kIGRpdGNoIGRyYWluYWdlIGludGVyYWN0IGluIHRoZSBkeW5hbWljcyBvZiBhIHdldGxhbmQgcGxhbnQgY29tbXVuaXR5KiBieSBHb29kd2lsbGllIGV0IGFsLiAyMDIwIEVjb3NwaGVyZS4gVGhlIGV4cGVyaW1lbnRhbCBkZXNpZ24gd2FzIGEgMngyIGZhY3RvcmlhbCBleHBlcmltZW50IHdpdGggMiBsZXZlbHMgb2YgZGlzdHVyYmFuY2UgKG1vd2VkIG9yIHVubW93ZWQpIGFuZCAyIGxldmVscyBvZiBudXRyaWVudCBhZGRpdGlvbiAoZmVydGlsaXplZCBhbmQgdW5mZXJ0aWxpemVkKS4gVGhlIGV4cGVyaW1lbnQgd2FzIGFycmFuZ2VkIGluIDggc3BhdGlhbCBibG9ja3Mgd2l0aCBlYWNoIGNvbnRhaW5pbmcgdGhlIDQgdHJlYXRtZW50IHBsb3RzLiBUaHJlZSBmaXhlZCBxdWFkcmF0cyB3ZXJlIGNyZWF0ZWQgd2l0aGluIGVhY2ggcGxvdCB0aGF0IHdlcmUgc2FtcGxlZCBhbm51YWxseS4gVGhlIHNwYXRpYWwgYmxvY2tzIHdlcmUgYXJyYW5nZWQgaW4gdHdvIHJvd3MgdGhhdCBkaWZmZXJlZCBpbiBwcm94aW1pdHkgdG8gYSBkaXRjaC4gIFlvdXIgY2hhbGxlbmdlIGlzIHRvIGRyYXcgYSBmaWd1cmUgdGhhdCBzaG93cyB0aGUgbWVhbiBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIGFscGhhIGRpdmVyc2l0eSBvdmVyIHRpbWUgKFllYXIpIGluIGEgd2F5IHRoYXQgaGlnaGxpZ2h0cyB0aGUgaW5kZXBlbmRlbnQgZWZmZWN0cyBvZiBNb3dpbmcsIEZlcnRpbGl6ZXIgdHJlYXRtZW50IGFuZCBwcm94aW1pdHkgdG8gdGhlIGRpdGNoLiAgDQoNCmBgYHtyfQ0KDQpgYGANCg0K