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