Chapter 1
prerequisites:
Load tidyverse by using library function
install.packages("tidyverse")
Error in install.packages : Updating loaded packages
library(tidyverse)
This one line loads the core tidyverse, packages that are needed. Question: Do cars with big engines more fuel efficient than smaller engines?
Load the mpg dataset
library(tidyverse)
package <U+393C><U+3E31>tidyverse<U+393C><U+3E32> was built under R version 3.3.3Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ------------------------------------------------------------------------------------
filter(): dplyr, stats
lag(): dplyr, stats
mpg
Notes: displ is the car’s engine size in liters hwy is the car’s fuel efficiency on the highway in miles per gallon (mpg)
Create the graph using ggplot
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy))

ggplot(data=mpg) creates an empty plot using the mpg dataset geom_point adds scatter point graph using displ on the x axis and hwy on the y axis
The template is: ggplot(data =) + (mapping =aes())
Exercises:
ggplot(data=mpg)

Answer: we see an empty plot How many rows in mtcars? How many columns?
dim(mtcars)
[1] 32 11
Answer: 32 rows, and 11 columns
summary(mtcars)
mpg cyl disp hp drat wt qsec
Min. :10.40 Min. :4.000 Min. : 71.1 Min. : 52.0 Min. :2.760 Min. :1.513 Min. :14.50
1st Qu.:15.43 1st Qu.:4.000 1st Qu.:120.8 1st Qu.: 96.5 1st Qu.:3.080 1st Qu.:2.581 1st Qu.:16.89
Median :19.20 Median :6.000 Median :196.3 Median :123.0 Median :3.695 Median :3.325 Median :17.71
Mean :20.09 Mean :6.188 Mean :230.7 Mean :146.7 Mean :3.597 Mean :3.217 Mean :17.85
3rd Qu.:22.80 3rd Qu.:8.000 3rd Qu.:326.0 3rd Qu.:180.0 3rd Qu.:3.920 3rd Qu.:3.610 3rd Qu.:18.90
Max. :33.90 Max. :8.000 Max. :472.0 Max. :335.0 Max. :4.930 Max. :5.424 Max. :22.90
vs am gear carb
Min. :0.0000 Min. :0.0000 Min. :3.000 Min. :1.000
1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:3.000 1st Qu.:2.000
Median :0.0000 Median :0.0000 Median :4.000 Median :2.000
Mean :0.4375 Mean :0.4062 Mean :3.688 Mean :2.812
3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:4.000 3rd Qu.:4.000
Max. :1.0000 Max. :1.0000 Max. :5.000 Max. :8.000
Shows eleven fields. To find out what drv variables describes, use the help function
?mpg
Pops up the help that shows up on the help screen:
drv f = front-wheel drive, r = rear wheel drive, 4 = 4wd
Exercise Make a scatterplot of hwy versus cyl
ggplot(data=mpg) + geom_point(mapping = aes(x=cyl, y=hwy))

What happens when we make a scatterplot of class versus drv?
ggplot(data=mpg) + geom_point(mapping = aes(x=class, y=drv))

Why is this plot not useful? (no pattern??)
Looking at the first scatterplot
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy))

You wont easily be able to identify the outliers. Why dont we apply colors to this plot?
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color=class))

This now quickly helps readers to find that 2seaters are more fuel efficient. Other ways will be to use size, alpha(transparency) and shape.
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, size=class))

ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy,alpha=class))

ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, shape=class))

Note that for the shape , SUV is gone! Ggplot will only use 6 shapes at a time.
You can also manually set properties
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy), color="red")

Note: for manually setting objects, do it outside the AES
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy), shape=24)

Why is the graph below not blue?
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color="blue"))

What happens when we map the same variable to different aesthetics? (size, shape, color)
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color=class, shape=class, size=class))

?geom_point
What is the stroke aesthetic do? Use the stroke aesthetic to modify the width of the border
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, shape=class,stroke=1))

What happens when you map an aethtic to something other than a variable name like aes(color =displ <5)
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color= displ<5))

How to make small multiple graphs (facets in R ggplot convention)
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color= displ <5)) +
facet_wrap(~ class, nrow=2)

To face on a combination of two variables, add facet_grid to your plot call.
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color= displ <5)) +
facet_grid(drv ~ cyl)

Use [.] to avoid facet on either row or column. Like this below:
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color= displ <5)) +
facet_grid(. ~ cyl)

What happes if we facet on a continuous variable?
first find which variable is continuous.
summary(mpg)
manufacturer model displ year cyl trans
Length:234 Length:234 Min. :1.600 Min. :1999 Min. :4.000 Length:234
Class :character Class :character 1st Qu.:2.400 1st Qu.:1999 1st Qu.:4.000 Class :character
Mode :character Mode :character Median :3.300 Median :2004 Median :6.000 Mode :character
Mean :3.472 Mean :2004 Mean :5.889
3rd Qu.:4.600 3rd Qu.:2008 3rd Qu.:8.000
Max. :7.000 Max. :2008 Max. :8.000
drv cty hwy fl class
Length:234 Min. : 9.00 Min. :12.00 Length:234 Length:234
Class :character 1st Qu.:14.00 1st Qu.:18.00 Class :character Class :character
Mode :character Median :17.00 Median :24.00 Mode :character Mode :character
Mean :16.86 Mean :23.44
3rd Qu.:19.00 3rd Qu.:27.00
Max. :35.00 Max. :44.00
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy, color= displ <5)) +
facet_grid(drv ~ cty)

A geom is the geometrical object that a plot uss to represent data. Using geom_point
ggplot(data=mpg) + geom_point(mapping = aes(x=displ, y=hwy))

Using geom_smooth with no arguments
ggplot(data=mpg) + geom_smooth(mapping = aes(x=displ, y=hwy))
Using geom_smooth with linetype argument
ggplot(data=mpg) + geom_smooth(mapping = aes(x=displ, y=hwy, linetype =drv))

The above geom_smooth separates the cars into 3 lines based on their drv value which describes a car’s drivetrain. We can overly geom_point on geom_smooth
ggplot(data=mpg) + geom_smooth(mapping = aes(x=displ, y=hwy, linetype =drv)) +
geom_point(mapping = aes(x=displ, y=hwy, color=drv))

To avoid having to set the mappings in each geom, you can place a set of mapping in the main ggplot(). Then they become ‘global’
ggplot(data=mpg, mapping=aes(x=displ,y=hwy)) + geom_point() + geom_smooth()

If you place argument inside the geom function, it will be treated as local.
ggplot(data=mpg, mapping=aes(x=displ,y=hwy)) +
geom_point(mapping=aes(color=class)) +
geom_smooth()

using this technic we can use it to specify a different data for each layer.
ggplot(data=mpg, mapping=aes(x=displ,y=hwy)) +
geom_point(mapping=aes(color=class)) +
geom_smooth(data = filter(mpg, class =='subcompact'),se=FALSE)

Exercises:
How to draw a line chart, a boxplot, a histogram, an area chart?
ggplot(data=mpg, mapping=aes(x=displ,y=hwy)) +
geom_line()

ggplot(data=mpg, mapping=aes(x=displ,y=hwy)) +
geom_boxplot()

ggplot(data=mpg, mapping=aes(x=displ)) +
geom_histogram()

ggplot(data=mpg, mapping=aes(x=displ,y=hwy)) +
geom_area()

Exercise:
ggplot(data=mpg, mapping=aes(x=displ,y=hwy, color=drv)) +
geom_point() +
geom_smooth(se =FALSE)

Q:What does show_legend do? What happens when it is removed?
ggplot(data=mpg, mapping=aes(x=displ,y=hwy, color=drv)) +
geom_point() +
geom_smooth(se =FALSE,show.legend = FALSE)

The legend on the DRV for the line doesnt showup.
Recreating the lines in the exercises
ggplot(data=mpg, mapping=aes(x=displ,y=hwy))+
geom_point(size=4)+
geom_smooth(se =FALSE)

ggplot(data=mpg, mapping=aes(x=displ,y=hwy))+
geom_point(size=4)+
geom_line(mapping = aes(linetype =drv))

To learn about ggplot: http://www.ggplot2-exts.org/ Cheat sheet: https://www.rstudio.com/resources/cheatsheets/
Bar Charts
ggplot(data=diamonds) +
geom_bar(mapping = aes(x=cut))

Stat_count() can be interchanged with geom_bar since geom_bar also uses stat_count to statistically transform the data into counts.
ggplot(data=diamonds) +
stat_count(mapping = aes(x=cut))

This works because every geom has a default stat, and every stat has a default geom. Three reasons to use a stat explicitly: 1. you want to overrid the default stat. 2. you want to to use aesthetics instead of transformed variables 3. you want to draw attention to the statistical transformation in the code.
demo <- tribble(
~a, ~b,
"Bar_1",20,
"Bar_2",30,
"Bar_3", 40)
ggplot(data = demo)+
geom_bar(
mapping=aes(x=a, y=b), stat="identity"
)

using aesthetics instead of transformed variables
ggplot(data = diamonds)+
geom_bar(
mapping=aes(x=cut, y=..prop.., group=1)
)

Case 3 demo
ggplot(data = diamonds)+
stat_summary(
mapping=aes(x=cut, y=depth),
fun.ymin=min,
fun.ymax=max,
fun.y=median
)

How is geom_col() different from geom_bar ? answer: geom_bar uses only one variable, geom_col uses two variables and displays the count that applies to both conditions.
ggplot(data = diamonds)+
geom_col(
mapping=aes(x=cut,y=carat)
)

ggplot(data=diamonds) +
geom_bar(mapping=aes(x=cut,fill=color, y=..prop..))

Position Adjustments for bar charts using either aesthetic
ggplot(data=diamonds) +
geom_bar(mapping=aes(x=cut,color=cut))

Position Adjustments for bar charts using fill
ggplot(data=diamonds) +
geom_bar(mapping=aes(x=cut,fill=color))

What happens when we fill using another variable like clarity
ggplot(data=diamonds) +
geom_bar(mapping=aes(x=cut,fill=clarity))

If we don’t want a stacked bar chart… the alpha option enables transparentcy so the overlaps can be seen.
ggplot(data=diamonds,mapping=aes(x=cut,fill=clarity)) +
geom_bar(aplha=1/5, position="identity"
)
Ignoring unknown parameters: aplha

using fill with NA values
ggplot(data=diamonds,mapping=aes(x=cut,color=clarity)) +
geom_bar(fill=NA, position="identity")

NA
To make it easier to compare proportions across groups, use position=“fill”
ggplot(data=diamonds) +
geom_bar(mapping=aes(x=cut,fill=clarity), position="fill")

Use position=“dodge” to place the bars beside each other
ggplot(data=diamonds) +
geom_bar(mapping=aes(x=cut,fill=clarity), position="dodge")

Using Jitter to show all the data in a scatter plot
ggplot(data=mpg) +
geom_point( mapping=aes(x=displ, y=hwy), position="jitter")

Adding randomness may be a strange way to improve the plot, but while it makes the graph less accurate, it makes your graph MORE revealing at large scales.
Coordinate System
The default coordinate is the cartesian coordinate system where the x and y position act independently to find the location of each point. some other coordinate systems: coord_flip() switches the x and y axes. Useful when you want horizontal box plots. coord_quickmap() sets the aspect ratio correctly for maps. Important for plotting spatial data coord_polar() uses polar coordinates. It reveals an interesting connection between a bar chart and a coxcomb chart.
# example of coord_flip
ggplot(data =mpg, mapping=aes(x=class, y=hwy))+
geom_boxplot()

ggplot(data =mpg, mapping=aes(x=class, y=hwy))+
geom_boxplot()+
coord_flip()

# exaple of quickmap()
library(ggplot2)
ph <- map_data("nz")
ggplot(ph, aes(long,lat, group=group))+
geom_polygon(fill="white", color="black")

ggplot(ph, aes(long,lat, group=group))+
geom_polygon(fill="white", color="black")+
coord_quickmap()

# example of polar coordiantes
bar <- ggplot(data=diamonds)+
geom_bar(mapping =aes(x=cut,fill=cut),show.legend=FALSE, width =1) +
theme(aspect.ratio=1)+
labs(x=NULL, y=NULL)
bar + coord_flip()

bar + coord_polar()

Layered grammar of graphics
The seven parameters template:
ggplot(data=datafile) + geom_function( mapping=aes(mappingsettings) stat=statsettings, position=positionsetting)+ coordinate_function+ facet_function )
LS0tDQp0aXRsZTogIlIgZm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8aDE+Q2hhcHRlciAxIDwvaDE+DQo8aDI+cHJlcmVxdWlzaXRlczo8L2gyPg0KDQpMb2FkIHRpZHl2ZXJzZSBieSB1c2luZyBsaWJyYXJ5IGZ1bmN0aW9uDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpgYGANCg0KVGhpcyBvbmUgbGluZSBsb2FkcyB0aGUgY29yZSB0aWR5dmVyc2UsIHBhY2thZ2VzIHRoYXQgYXJlIG5lZWRlZC4NClF1ZXN0aW9uOiBEbyBjYXJzIHdpdGggYmlnIGVuZ2luZXMgbW9yZSBmdWVsIGVmZmljaWVudCB0aGFuIHNtYWxsZXIgZW5naW5lcz8NCg0KTG9hZCB0aGUgbXBnIGRhdGFzZXQNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KYGBgDQoNCmBgYHtyfQ0KbXBnDQpgYGANCg0KTm90ZXM6IA0KZGlzcGwgaXMgdGhlIGNhcidzIGVuZ2luZSBzaXplIGluIGxpdGVycw0KaHd5IGlzIHRoZSBjYXIncyBmdWVsIGVmZmljaWVuY3kgb24gdGhlIGhpZ2h3YXkgaW4gbWlsZXMgcGVyIGdhbGxvbiAobXBnKQ0KDQpDcmVhdGUgdGhlIGdyYXBoIHVzaW5nIGdncGxvdA0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3kpKQ0KYGBgDQoNCmdncGxvdChkYXRhPW1wZykgY3JlYXRlcyBhbiBlbXB0eSBwbG90IHVzaW5nIHRoZSBtcGcgZGF0YXNldA0KZ2VvbV9wb2ludCBhZGRzIHNjYXR0ZXIgcG9pbnQgZ3JhcGggdXNpbmcgZGlzcGwgb24gdGhlIHggYXhpcyBhbmQgaHd5IG9uIHRoZSB5IGF4aXMNCg0KVGhlIHRlbXBsYXRlIGlzOg0KZ2dwbG90KGRhdGEgPTxkYXRhPikgKyA8Z2VvbV9mdW5jdGlvbj4gKG1hcHBpbmcgPWFlcyg8TUFQUElOR1M+KSkNCg0KRXhlcmNpc2VzOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpDQpgYGANCg0KQW5zd2VyOiB3ZSBzZWUgYW4gZW1wdHkgcGxvdDwvYnI+DQpIb3cgbWFueSByb3dzIGluIG10Y2Fycz8gSG93IG1hbnkgY29sdW1ucz8gPC9icj4NCmBgYHtyfQ0KDQpkaW0obXRjYXJzKQ0KYGBgDQpBbnN3ZXI6IDMyIHJvd3MsIGFuZCAxMSBjb2x1bW5zDQoNCmBgYHtyfQ0Kc3VtbWFyeShtdGNhcnMpDQpgYGANCg0KU2hvd3MgZWxldmVuIGZpZWxkcy48L2JyPg0KVG8gZmluZCBvdXQgd2hhdCBkcnYgdmFyaWFibGVzIGRlc2NyaWJlcywgdXNlIHRoZSBoZWxwIGZ1bmN0aW9uPC9icj4NCmBgYHtyfQ0KP21wZw0KDQpgYGANClBvcHMgdXAgdGhlIGhlbHAgdGhhdCBzaG93cyB1cCBvbiB0aGUgaGVscCBzY3JlZW46PC9icj4NCg0KZHJ2ICBmID0gZnJvbnQtd2hlZWwgZHJpdmUsIHIgPSByZWFyIHdoZWVsIGRyaXZlLCA0ID0gNHdkPC9icj4NCg0KRXhlcmNpc2UgTWFrZSBhIHNjYXR0ZXJwbG90IG9mIGh3eSB2ZXJzdXMgY3lsPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9Y3lsLCB5PWh3eSkpDQpgYGANCg0KDQpXaGF0IGhhcHBlbnMgd2hlbiB3ZSBtYWtlIGEgc2NhdHRlcnBsb3Qgb2YgY2xhc3MgdmVyc3VzIGRydj88L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1jbGFzcywgeT1kcnYpKQ0KYGBgDQoNCldoeSBpcyB0aGlzIHBsb3Qgbm90IHVzZWZ1bD8gPC9icj4NCihubyBwYXR0ZXJuPz8pPC9icj4NCg0KTG9va2luZyBhdCB0aGUgZmlyc3Qgc2NhdHRlcnBsb3Q8L2JyPg0KYGBge3J9DQoNCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3kpKQ0KDQoNCmBgYA0KWW91IHdvbnQgZWFzaWx5IGJlIGFibGUgdG8gaWRlbnRpZnkgdGhlIG91dGxpZXJzLiBXaHkgZG9udCB3ZSBhcHBseSBjb2xvcnMgdG8gdGhpcyBwbG90PzwvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PWRpc3BsLCB5PWh3eSwgY29sb3I9Y2xhc3MpKQ0KYGBgDQoNClRoaXMgbm93IHF1aWNrbHkgaGVscHMgcmVhZGVycyB0byBmaW5kIHRoYXQgMnNlYXRlcnMgYXJlIG1vcmUgZnVlbCBlZmZpY2llbnQuPC9icj4NCk90aGVyIHdheXMgd2lsbCBiZSB0byB1c2Ugc2l6ZSwgYWxwaGEodHJhbnNwYXJlbmN5KSBhbmQgc2hhcGUuPC9icj4NCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PWRpc3BsLCB5PWh3eSwgc2l6ZT1jbGFzcykpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5LGFscGhhPWNsYXNzKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3ksIHNoYXBlPWNsYXNzKSkNCmBgYA0KDQpOb3RlIHRoYXQgZm9yIHRoZSBzaGFwZSAsIFNVViBpcyBnb25lISBHZ3Bsb3Qgd2lsbCBvbmx5IHVzZSA2IHNoYXBlcyBhdCBhIHRpbWUuIDwvYnI+DQoNCllvdSBjYW4gYWxzbyBtYW51YWxseSBzZXQgcHJvcGVydGllczwvYnI+DQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3kpLCBjb2xvcj0icmVkIikNCmBgYA0KDQpOb3RlOiBmb3IgbWFudWFsbHkgc2V0dGluZyBvYmplY3RzLCBkbyBpdCBvdXRzaWRlIHRoZSBBRVMgPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5KSwgc2hhcGU9MjQpDQpgYGANCg0KV2h5IGlzIHRoZSBncmFwaCBiZWxvdyBub3QgYmx1ZT88L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3ksIGNvbG9yPSJibHVlIikpDQpgYGANCg0KV2hhdCBoYXBwZW5zIHdoZW4gd2UgbWFwIHRoZSBzYW1lIHZhcmlhYmxlIHRvIGRpZmZlcmVudCBhZXN0aGV0aWNzPyAoc2l6ZSwgc2hhcGUsIGNvbG9yKTwvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PWRpc3BsLCB5PWh3eSwgY29sb3I9Y2xhc3MsIHNoYXBlPWNsYXNzLCBzaXplPWNsYXNzKSkNCmBgYA0KYGBge3J9DQo/Z2VvbV9wb2ludA0KYGBgDQoNCldoYXQgaXMgdGhlIHN0cm9rZSBhZXN0aGV0aWMgZG8/PC9icj4NClVzZSB0aGUgc3Ryb2tlIGFlc3RoZXRpYyB0byBtb2RpZnkgdGhlIHdpZHRoIG9mIHRoZSBib3JkZXI8L2JyPg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5LCBzaGFwZT1jbGFzcyxzdHJva2U9MSkpDQpgYGANCg0KV2hhdCBoYXBwZW5zIHdoZW4geW91IG1hcCBhbiBhZXRodGljIHRvIHNvbWV0aGluZyBvdGhlciB0aGFuIGEgdmFyaWFibGUgbmFtZSBsaWtlIGFlcyhjb2xvciA9ZGlzcGwgPDUpPC9icj4NCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PWRpc3BsLCB5PWh3eSwgY29sb3I9IGRpc3BsPDUpKQ0KYGBgDQoNCkhvdyB0byBtYWtlIHNtYWxsIG11bHRpcGxlIGdyYXBocyAoZmFjZXRzIGluIFIgZ2dwbG90IGNvbnZlbnRpb24pPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5LCBjb2xvcj0gZGlzcGwgPDUpKSArDQogIGZhY2V0X3dyYXAofiBjbGFzcywgbnJvdz0yKQ0KYGBgDQoNClRvIGZhY2Ugb24gYSBjb21iaW5hdGlvbiBvZiB0d28gdmFyaWFibGVzLCBhZGQgZmFjZXRfZ3JpZCB0byB5b3VyIHBsb3QgY2FsbC48L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3ksIGNvbG9yPSBkaXNwbCA8NSkpICsNCiAgZmFjZXRfZ3JpZChkcnYgfiBjeWwpDQpgYGANCg0KVXNlIFsuXSB0byBhdm9pZCBmYWNldCBvbiBlaXRoZXIgcm93IG9yIGNvbHVtbi4gTGlrZSB0aGlzIGJlbG93OjwvYnI+DQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5LCBjb2xvcj0gZGlzcGwgPDUpKSArDQogIGZhY2V0X2dyaWQoLiB+IGN5bCkNCmBgYA0KDQpXaGF0IGhhcHBlcyBpZiB3ZSBmYWNldCBvbiBhIGNvbnRpbnVvdXMgdmFyaWFibGU/PC9icj4NCg0KZmlyc3QgZmluZCB3aGljaCB2YXJpYWJsZSBpcyBjb250aW51b3VzLjwvYnI+DQoNCmBgYHtyfQ0Kc3VtbWFyeShtcGcpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5LCBjb2xvcj0gZGlzcGwgPDUpKSArDQogIGZhY2V0X2dyaWQoZHJ2IH4gY3R5KQ0KYGBgDQoNCiBBIGdlb20gaXMgdGhlIGdlb21ldHJpY2FsIG9iamVjdCB0aGF0IGEgcGxvdCB1c3MgdG8gcmVwcmVzZW50IGRhdGEuIDwvYnI+DQogVXNpbmcgZ2VvbV9wb2ludDwvYnI+DQogDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3kpKQ0KYGBgDQoNCg0KVXNpbmcgZ2VvbV9zbW9vdGggd2l0aCBubyBhcmd1bWVudHM8L2JyPg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4PWRpc3BsLCB5PWh3eSkpDQpgYGANCg0KDQpVc2luZyBnZW9tX3Ntb290aCB3aXRoIGxpbmV0eXBlIGFyZ3VtZW50DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSArIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeD1kaXNwbCwgeT1od3ksIGxpbmV0eXBlID1kcnYpKQ0KYGBgDQpUaGUgYWJvdmUgZ2VvbV9zbW9vdGggc2VwYXJhdGVzIHRoZSBjYXJzIGludG8gMyBsaW5lcyBiYXNlZCBvbiB0aGVpciBkcnYgdmFsdWUgd2hpY2ggZGVzY3JpYmVzIGEgY2FyJ3MgZHJpdmV0cmFpbi48L2JyPg0KV2UgY2FuIG92ZXJseSBnZW9tX3BvaW50IG9uIGdlb21fc21vb3RoPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpICsgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4PWRpc3BsLCB5PWh3eSwgbGluZXR5cGUgPWRydikpICsNCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9ZGlzcGwsIHk9aHd5LCBjb2xvcj1kcnYpKQ0KYGBgDQoNClRvIGF2b2lkIGhhdmluZyB0byBzZXQgdGhlIG1hcHBpbmdzIGluIGVhY2ggZ2VvbSwgeW91IGNhbiBwbGFjZSBhIHNldCBvZiBtYXBwaW5nIGluIHRoZSBtYWluIGdncGxvdCgpLjwvYnI+IFRoZW4gdGhleSBiZWNvbWUgJ2dsb2JhbCc8L2JyPg0KDQpgYGB7cn0NCg0KZ2dwbG90KGRhdGE9bXBnLCBtYXBwaW5nPWFlcyh4PWRpc3BsLHk9aHd5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpDQoNCmBgYA0KDQpJZiB5b3UgcGxhY2UgYXJndW1lbnQgaW5zaWRlIHRoZSBnZW9tIGZ1bmN0aW9uLCBpdCB3aWxsIGJlIHRyZWF0ZWQgYXMgbG9jYWwuPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcsIG1hcHBpbmc9YWVzKHg9ZGlzcGwseT1od3kpKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKGNvbG9yPWNsYXNzKSkgKyANCiAgZ2VvbV9zbW9vdGgoKQ0KYGBgDQoNCnVzaW5nIHRoaXMgdGVjaG5pYyB3ZSBjYW4gdXNlIGl0IHRvIHNwZWNpZnkgYSBkaWZmZXJlbnQgZGF0YSBmb3IgZWFjaCBsYXllci48L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZywgbWFwcGluZz1hZXMoeD1kaXNwbCx5PWh3eSkpICsgDQogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoY29sb3I9Y2xhc3MpKSArIA0KICBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKG1wZywgY2xhc3MgPT0nc3ViY29tcGFjdCcpLHNlPUZBTFNFKQ0KYGBgDQoNCkV4ZXJjaXNlczo8L2JyPg0KDQpIb3cgdG8gZHJhdyBhIGxpbmUgY2hhcnQsIGEgYm94cGxvdCwgYSBoaXN0b2dyYW0sIGFuIGFyZWEgY2hhcnQ/PC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcsIG1hcHBpbmc9YWVzKHg9ZGlzcGwseT1od3kpKSArIA0KICBnZW9tX2xpbmUoKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZywgbWFwcGluZz1hZXMoeD1kaXNwbCx5PWh3eSkpICsgDQogIGdlb21fYm94cGxvdCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcsIG1hcHBpbmc9YWVzKHg9ZGlzcGwpKSArIA0KICBnZW9tX2hpc3RvZ3JhbSgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcsIG1hcHBpbmc9YWVzKHg9ZGlzcGwseT1od3kpKSArIA0KICBnZW9tX2FyZWEoKQ0KYGBgDQoNCkV4ZXJjaXNlOiA8L2JyPg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcsIG1hcHBpbmc9YWVzKHg9ZGlzcGwseT1od3ksIGNvbG9yPWRydikpICsgDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKHNlID1GQUxTRSkNCg0KYGBgDQoNClE6V2hhdCBkb2VzIHNob3dfbGVnZW5kIGRvPyBXaGF0IGhhcHBlbnMgd2hlbiBpdCBpcyByZW1vdmVkPzwvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnLCBtYXBwaW5nPWFlcyh4PWRpc3BsLHk9aHd5LCBjb2xvcj1kcnYpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChzZSA9RkFMU0Usc2hvdy5sZWdlbmQgPSBGQUxTRSkNCmBgYA0KVGhlIGxlZ2VuZCBvbiB0aGUgRFJWIGZvciB0aGUgbGluZSBkb2VzbnQgc2hvd3VwLjwvYnI+DQoNClJlY3JlYXRpbmcgdGhlIGxpbmVzIGluIHRoZSBleGVyY2lzZXM8L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZywgbWFwcGluZz1hZXMoeD1kaXNwbCx5PWh3eSkpKw0KICBnZW9tX3BvaW50KHNpemU9NCkrDQogIGdlb21fc21vb3RoKHNlID1GQUxTRSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZywgbWFwcGluZz1hZXMoeD1kaXNwbCx5PWh3eSkpKw0KICBnZW9tX3BvaW50KHNpemU9NCkrDQogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKGxpbmV0eXBlID1kcnYpKQ0KYGBgDQoNClRvIGxlYXJuIGFib3V0IGdncGxvdDogaHR0cDovL3d3dy5nZ3Bsb3QyLWV4dHMub3JnLyANCkNoZWF0IHNoZWV0OiAgaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLyANCg0KDQpCYXIgQ2hhcnRzIA0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1kaWFtb25kcykgKyANCiBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHg9Y3V0KSkNCmBgYA0KDQoNClN0YXRfY291bnQoKSBjYW4gYmUgaW50ZXJjaGFuZ2VkIHdpdGggZ2VvbV9iYXIgc2luY2UgZ2VvbV9iYXIgYWxzbyB1c2VzIHN0YXRfY291bnQgdG8gc3RhdGlzdGljYWxseSB0cmFuc2Zvcm0gdGhlIGRhdGEgaW50byBjb3VudHMuPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1kaWFtb25kcykgKyANCiBzdGF0X2NvdW50KG1hcHBpbmcgPSBhZXMoeD1jdXQpKQ0KYGBgDQoNClRoaXMgd29ya3MgYmVjYXVzZSBldmVyeSBnZW9tIGhhcyBhIGRlZmF1bHQgc3RhdCwgYW5kIGV2ZXJ5IHN0YXQgaGFzIGEgZGVmYXVsdCBnZW9tLiA8L2JyPg0KVGhyZWUgcmVhc29ucyB0byB1c2UgYSBzdGF0IGV4cGxpY2l0bHk6PC9icj4NCjEuIHlvdSB3YW50IHRvIG92ZXJyaWQgdGhlIGRlZmF1bHQgc3RhdC48L2JyPg0KMi4geW91IHdhbnQgdG8gdG8gdXNlIGFlc3RoZXRpY3MgaW5zdGVhZCBvZiB0cmFuc2Zvcm1lZCB2YXJpYWJsZXM8L2JyPg0KMy4geW91IHdhbnQgdG8gZHJhdyBhdHRlbnRpb24gdG8gdGhlIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIGluIHRoZSBjb2RlLjwvYnI+DQoNCmBgYHtyfQ0KZGVtbyA8LSB0cmliYmxlKA0KICB+YSwgfmIsDQogICJCYXJfMSIsMjAsDQogICJCYXJfMiIsMzAsDQogICJCYXJfMyIsIDQwKQ0KDQpnZ3Bsb3QoZGF0YSA9IGRlbW8pKw0KICBnZW9tX2JhcigNCiAgICBtYXBwaW5nPWFlcyh4PWEsIHk9YiksIHN0YXQ9ImlkZW50aXR5Ig0KICApDQpgYGANCg0KDQp1c2luZyBhZXN0aGV0aWNzIGluc3RlYWQgb2YgdHJhbnNmb3JtZWQgdmFyaWFibGVzDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykrDQogIGdlb21fYmFyKA0KICAgIG1hcHBpbmc9YWVzKHg9Y3V0LCB5PS4ucHJvcC4uLCBncm91cD0xKQ0KICApDQpgYGANCg0KQ2FzZSAzIGRlbW8NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSsNCiAgc3RhdF9zdW1tYXJ5KA0KICAgIG1hcHBpbmc9YWVzKHg9Y3V0LCB5PWRlcHRoKSwNCiAgICBmdW4ueW1pbj1taW4sDQogICAgZnVuLnltYXg9bWF4LA0KICAgIGZ1bi55PW1lZGlhbg0KICApDQoNCmBgYA0KDQpIb3cgaXMgZ2VvbV9jb2woKSBkaWZmZXJlbnQgZnJvbSBnZW9tX2JhciA/PC9icj4NCmFuc3dlcjogZ2VvbV9iYXIgdXNlcyBvbmx5IG9uZSB2YXJpYWJsZSwgZ2VvbV9jb2wgdXNlcyB0d28gdmFyaWFibGVzIGFuZCBkaXNwbGF5cyB0aGUgY291bnQgdGhhdCBhcHBsaWVzIHRvIGJvdGggY29uZGl0aW9ucy48L2JyPg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykrDQogIGdlb21fY29sKA0KICAgIG1hcHBpbmc9YWVzKHg9Y3V0LHk9Y2FyYXQpDQogICkNCg0KDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPWRpYW1vbmRzKSArDQogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9Y3V0LGZpbGw9Y29sb3IsIHk9Li5wcm9wLi4pKQ0KYGBgDQoNCg0KUG9zaXRpb24gQWRqdXN0bWVudHMgZm9yIGJhciBjaGFydHMgdXNpbmcgZWl0aGVyIGFlc3RoZXRpYyA8L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPWRpYW1vbmRzKSArDQogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9Y3V0LGNvbG9yPWN1dCkpDQpgYGANCg0KUG9zaXRpb24gQWRqdXN0bWVudHMgZm9yIGJhciBjaGFydHMgdXNpbmcgZmlsbA0KPC9icj4NCmBgYHtyfQ0KZ2dwbG90KGRhdGE9ZGlhbW9uZHMpICsNCiAgZ2VvbV9iYXIobWFwcGluZz1hZXMoeD1jdXQsZmlsbD1jb2xvcikpDQpgYGANCg0KV2hhdCBoYXBwZW5zIHdoZW4gd2UgZmlsbCB1c2luZyBhbm90aGVyIHZhcmlhYmxlIGxpa2UgY2xhcml0eTwvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9ZGlhbW9uZHMpICsNCiAgZ2VvbV9iYXIobWFwcGluZz1hZXMoeD1jdXQsZmlsbD1jbGFyaXR5KSkNCmBgYA0KDQpJZiB3ZSBkb24ndCB3YW50IGEgc3RhY2tlZCBiYXIgY2hhcnQuLi48L2JyPg0KdGhlIGFscGhhIG9wdGlvbiBlbmFibGVzIHRyYW5zcGFyZW50Y3kgc28gdGhlIG92ZXJsYXBzIGNhbiBiZSBzZWVuLjwvYnI+DQpgYGB7cn0NCmdncGxvdChkYXRhPWRpYW1vbmRzLG1hcHBpbmc9YWVzKHg9Y3V0LGZpbGw9Y2xhcml0eSkpICsNCiAgZ2VvbV9iYXIoYXBsaGE9MS81LCBwb3NpdGlvbj0iaWRlbnRpdHkiDQogICkNCmBgYA0KDQp1c2luZyBmaWxsIHdpdGggTkEgdmFsdWVzPC9icj4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1kaWFtb25kcyxtYXBwaW5nPWFlcyh4PWN1dCxjb2xvcj1jbGFyaXR5KSkgKw0KICBnZW9tX2JhcihmaWxsPU5BLCBwb3NpdGlvbj0iaWRlbnRpdHkiKQ0KICANCmBgYA0KDQpUbyBtYWtlIGl0IGVhc2llciB0byBjb21wYXJlIHByb3BvcnRpb25zIGFjcm9zcyBncm91cHMsIHVzZSBwb3NpdGlvbj0iZmlsbCI8L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPWRpYW1vbmRzKSArDQogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9Y3V0LGZpbGw9Y2xhcml0eSksIHBvc2l0aW9uPSJmaWxsIikNCmBgYA0KDQpVc2UgcG9zaXRpb249ImRvZGdlIiB0byBwbGFjZSB0aGUgYmFycyBiZXNpZGUgZWFjaCBvdGhlcjwvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9ZGlhbW9uZHMpICsNCiAgZ2VvbV9iYXIobWFwcGluZz1hZXMoeD1jdXQsZmlsbD1jbGFyaXR5KSwgcG9zaXRpb249ImRvZGdlIikNCmBgYA0KDQpVc2luZyBKaXR0ZXIgdG8gc2hvdyBhbGwgdGhlIGRhdGEgaW4gYSBzY2F0dGVyIHBsb3Q8L2JyPg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykgKw0KICBnZW9tX3BvaW50KCBtYXBwaW5nPWFlcyh4PWRpc3BsLCB5PWh3eSksIHBvc2l0aW9uPSJqaXR0ZXIiKQ0KYGBgDQoNCkFkZGluZyByYW5kb21uZXNzIG1heSBiZSBhIHN0cmFuZ2Ugd2F5IHRvIGltcHJvdmUgdGhlIHBsb3QsIGJ1dCB3aGlsZSBpdCBtYWtlcyB0aGUgZ3JhcGggbGVzcyBhY2N1cmF0ZSwgaXQgbWFrZXMgeW91ciBncmFwaCBNT1JFIHJldmVhbGluZyBhdCBsYXJnZSBzY2FsZXMuPC9icj4NCg0KDQo8aDI+Q29vcmRpbmF0ZSBTeXN0ZW0gPC9oMj4NClRoZSBkZWZhdWx0IGNvb3JkaW5hdGUgaXMgdGhlIGNhcnRlc2lhbiBjb29yZGluYXRlIHN5c3RlbSB3aGVyZSB0aGUgeCBhbmQgeSBwb3NpdGlvbiBhY3QgaW5kZXBlbmRlbnRseSB0byBmaW5kIHRoZSBsb2NhdGlvbiBvZiBlYWNoIHBvaW50LiBzb21lIG90aGVyIGNvb3JkaW5hdGUgc3lzdGVtczo8L2JyPg0KPC9icj4NCmNvb3JkX2ZsaXAoKSBzd2l0Y2hlcyB0aGUgeCBhbmQgeSBheGVzLiBVc2VmdWwgd2hlbiB5b3Ugd2FudCBob3Jpem9udGFsIGJveCBwbG90cy48L2JyPg0KY29vcmRfcXVpY2ttYXAoKSBzZXRzIHRoZSBhc3BlY3QgcmF0aW8gY29ycmVjdGx5IGZvciBtYXBzLiBJbXBvcnRhbnQgZm9yIHBsb3R0aW5nIHNwYXRpYWwgZGF0YTwvYnI+DQpjb29yZF9wb2xhcigpIHVzZXMgcG9sYXIgY29vcmRpbmF0ZXMuIEl0IHJldmVhbHMgYW4gaW50ZXJlc3RpbmcgY29ubmVjdGlvbiBiZXR3ZWVuIGEgYmFyIGNoYXJ0IGFuZCBhIGNveGNvbWIgY2hhcnQuPC9icj4NCg0KDQpgYGB7cn0NCiMgZXhhbXBsZSBvZiBjb29yZF9mbGlwDQpnZ3Bsb3QoZGF0YSA9bXBnLCBtYXBwaW5nPWFlcyh4PWNsYXNzLCB5PWh3eSkpKw0KICBnZW9tX2JveHBsb3QoKQ0KDQpnZ3Bsb3QoZGF0YSA9bXBnLCBtYXBwaW5nPWFlcyh4PWNsYXNzLCB5PWh3eSkpKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgY29vcmRfZmxpcCgpDQoNCg0KDQpgYGANCg0KDQpgYGB7cn0NCiMgZXhhcGxlIG9mIHF1aWNrbWFwKCkNCmxpYnJhcnkoZ2dwbG90MikNCg0KcGggPC0gbWFwX2RhdGEoIm56IikNCg0KZ2dwbG90KHBoLCBhZXMobG9uZyxsYXQsIGdyb3VwPWdyb3VwKSkrDQogIGdlb21fcG9seWdvbihmaWxsPSJ3aGl0ZSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChwaCwgYWVzKGxvbmcsbGF0LCBncm91cD1ncm91cCkpKw0KICBnZW9tX3BvbHlnb24oZmlsbD0id2hpdGUiLCBjb2xvcj0iYmxhY2siKSsNCiAgY29vcmRfcXVpY2ttYXAoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBleGFtcGxlIG9mIHBvbGFyIGNvb3JkaW5hdGVzDQoNCmJhciA8LSBnZ3Bsb3QoZGF0YT1kaWFtb25kcykrDQogIGdlb21fYmFyKG1hcHBpbmcgPWFlcyh4PWN1dCxmaWxsPWN1dCksc2hvdy5sZWdlbmQ9RkFMU0UsIHdpZHRoID0xKSArDQogIHRoZW1lKGFzcGVjdC5yYXRpbz0xKSsNCiAgbGFicyh4PU5VTEwsIHk9TlVMTCkNCg0KYmFyICsgY29vcmRfZmxpcCgpDQpiYXIgKyBjb29yZF9wb2xhcigpDQpgYGANCg0KPGgyPiBMYXllcmVkIGdyYW1tYXIgb2YgZ3JhcGhpY3MgPC9oMj4NCg0KVGhlIHNldmVuIHBhcmFtZXRlcnMgdGVtcGxhdGU6PC9icj4NCg0KZ2dwbG90KGRhdGE9ZGF0YWZpbGUpICsNCiBnZW9tX2Z1bmN0aW9uKA0KICAgbWFwcGluZz1hZXMobWFwcGluZ3NldHRpbmdzKQ0KICAgc3RhdD1zdGF0c2V0dGluZ3MsDQogICBwb3NpdGlvbj1wb3NpdGlvbnNldHRpbmcpKw0KICAgY29vcmRpbmF0ZV9mdW5jdGlvbisNCiAgIGZhY2V0X2Z1bmN0aW9uDQogKQ==