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==