This notebook explores the mpg dataset available in the ggplot2 package. The goals of this notebook are:

require(ggplot2)

This dataset provides fuel economy data from 1999 and 2008 for 38 popular models of cars. The dataset is shipped with ggplot2 package.

Variable Type Description Details
manufacturer string car manufacturer 15 manufacturers
model string model name 38 models
displ numeric engine displacement in liters 1.6 - 7.0, median: 3.3
year integer year of manufacturing 1999, 2008
cyl number of cylinders 4, 5, 6, 8
trans string type of transmission automatic, manual (many sub types)
drv string drive type f, r, 4, f=front wheel, r=rear wheel, 4=4 wheel
cty integer city mileage miles per gallon
hwy integer highway mileage miles per gallon
fl string fuel type 5 fuel types (diesel, petrol, electric, etc.)
class string vehicle class 7 types (compact, SUV, minivan etc.)

Description of mpg dataset

str(mpg)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   234 obs. of  11 variables:
 $ manufacturer: chr  "audi" "audi" "audi" "audi" ...
 $ model       : chr  "a4" "a4" "a4" "a4" ...
 $ displ       : num  1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
 $ year        : int  1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
 $ cyl         : int  4 4 4 4 6 6 6 4 4 4 ...
 $ trans       : chr  "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
 $ drv         : chr  "f" "f" "f" "f" ...
 $ cty         : int  18 21 20 21 16 18 18 18 16 20 ...
 $ hwy         : int  29 29 31 30 26 26 27 26 25 28 ...
 $ fl          : chr  "p" "p" "p" "p" ...
 $ class       : chr  "compact" "compact" "compact" "compact" ...

Number of rows or observations or records

nrow(mpg)
[1] 234

Number of columns or variables

ncol(mpg)
[1] 11

Names of columns

colnames(mpg)
 [1] "manufacturer" "model"        "displ"        "year"        
 [5] "cyl"          "trans"        "drv"          "cty"         
 [9] "hwy"          "fl"           "class"       

Manufacturers

table(mpg$manufacturer)

      audi  chevrolet      dodge       ford      honda    hyundai       jeep land rover 
        18         19         37         25          9         14          8          4 
   lincoln    mercury     nissan    pontiac     subaru     toyota volkswagen 
         3          4         13          5         14         34         27 
qplot(manufacturer, data=mpg, geom="bar", fill=manufacturer)

Year of manufacturing

table(mpg$year)

1999 2008 
 117  117 
qplot(factor(year), data=mpg, geom="bar", fill=factor(year))

Displacement

summary(mpg$displ)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.600   2.400   3.300   3.472   4.600   7.000 
qplot(displ, data=mpg, geom="histogram", bins=30)

boxplot(mpg$displ)

Cylinders

table(mpg$cyl)

 4  5  6  8 
81  4 79 70 
qplot(cyl, data=mpg, geom="bar", fill=factor(cyl))

Transmission types

table(mpg$trans)

  auto(av)   auto(l3)   auto(l4)   auto(l5)   auto(l6)   auto(s4)   auto(s5)   auto(s6) 
         5          2         83         39          6          3          3         16 
manual(m5) manual(m6) 
        58         19 
qplot(trans, data=mpg, geom="bar", fill=factor(trans))

Wheel drive types

table(mpg$drv)

  4   f   r 
103 106  25 
qplot(drv, data=mpg, geom="bar", fill=drv)

Mileage in city

summary(mpg$cty)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   9.00   14.00   17.00   16.86   19.00   35.00 
qplot(cty, data=mpg, geom="histogram", bins=20)

qplot(cty, data=mpg, geom="histogram", bins=30)

qplot(cty, data=mpg, geom="histogram", bins=40)

Mileage in highways

summary(mpg$hwy)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  12.00   18.00   24.00   23.44   27.00   44.00 
qplot(hwy, data=mpg, geom="histogram", bins=20)

qplot(hwy, data=mpg, geom="histogram", bins=30)

qplot(hwy, data=mpg, geom="histogram", bins=40)

Fuel types

table(mpg$fl)

  c   d   e   p   r 
  1   5   8  52 168 
qplot(fl, data=mpg, geom="bar", fill=fl)

Vehicle Class

table(mpg$class)

   2seater    compact    midsize    minivan     pickup subcompact        suv 
         5         47         41         11         33         35         62 
qplot(class, data=mpg, geom="bar", fill=class)

Displacement vs highway efficiency

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy), color='blue')

The same graph can be built using qplot too

qplot(displ, hwy, data=mpg, geom="point", color='red')

Looking at this data separately for each class

qplot(displ, hwy, data=mpg, geom="point", color=class)

Fixing the color for all points

qplot(displ, hwy, data=mpg, geom="point", color=I("blue"))

The same graphic through ggplot

ggplot(data=mpg) + geom_point(mapping = aes(x = displ, y = hwy), color = "blue")

Separate graphs for each vehicle class:

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color=class)) +
  facet_wrap(~ class, nrow = 2)

Creating facets on the basis of two variables : number of cylinders and type of drive

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color=drv)) +
  facet_grid(drv ~ cyl)

There are no cars for some combinations of drive and cylinders.

Creating facets vertically on drive:

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color=drv)) +
  facet_grid(drv ~ .)

Creating facets horizontally on cylinders:

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color=cyl)) +
  facet_grid(. ~ cyl)

Estimating a smooth curve for the relationship between displacement and highway mileage:

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

Separate curve for each type of drive:

ggplot(data = mpg) + 
  geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv, color=drv))

Grouping data:

ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, group = drv))

Grouping data using specific color:

ggplot(data = mpg) +
  geom_smooth(
    mapping = aes(x = displ, y = hwy, color = drv)
  )

Hiding the legend:

ggplot(data = mpg) +
  geom_smooth(
    mapping = aes(x = displ, y = hwy, color = drv),
    show.legend = FALSE
  )

Overlaying a smooth curve on top of scatter plot:

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

Using a color based grouping for the scatter plot but a common curve overlayed on top of it:

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

Filtering data for a specific geom:

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth(data = dplyr::filter(mpg, class == "subcompact"), se = FALSE)

The se=FALSE setting removes the confidence interval around the estimated curve.

Grouping data by drive and then drawing scatter plot with estimated curve for each group:

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

Drive vs Cylinder

Visualizing the combinations of drives and cylinders available in the dataset:

ggplot(data = mpg) + 
  geom_point(mapping = aes(y = drv, x = cyl, color=factor(cyl)), size=4)

Is automatic transmission better?

We will create a copy of our data frame

mpg2 <- mpg

We will introduce a new column defining whether the car is automatic or manual

mpg2$is.automatic <- startsWith(mpg2$trans, 'auto')
mpg2$transmission <- ifelse(mpg2$is.automatic, 'auto', 'man')

Let’s verify the statistics

table(mpg2$trans)

  auto(av)   auto(l3)   auto(l4)   auto(l5)   auto(l6)   auto(s4) 
         5          2         83         39          6          3 
  auto(s5)   auto(s6) manual(m5) manual(m6) 
         3         16         58         19 
table(mpg2$is.automatic)

FALSE  TRUE 
   77   157 

Let’s compare the box plots of city mileage for the two types

qplot(transmission, cty, data=mpg2, geom='boxplot', fill=transmission)

This graphic suggests that the manual transmission is better than automatic one.

It is time for us to perform a t-test to verify the accuracy.

manual.cty <- mpg2$cty[!mpg2$is.automatic]
auto.cty <- mpg2$cty[mpg2$is.automatic]
t.test(manual.cty, auto.cty, alternative = "two.sided", var.equal = FALSE)

    Welch Two Sample t-test

data:  manual.cty and auto.cty
t = 4.5375, df = 132.32, p-value = 1.263e-05
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 1.527033 3.887311
sample estimates:
mean of x mean of y 
 18.67532  15.96815 

The p-value is very low. It indicates strong evidence against the null hypothesis, so we reject the null hypothesis and accept that means are not equal.

Let’s also verify the hypothesis that mean of manual is greater than mean of automatic transmission.

t.test(manual.cty, auto.cty, alternative = "greater", var.equal = FALSE)

    Welch Two Sample t-test

data:  manual.cty and auto.cty
t = 4.5375, df = 132.32, p-value = 6.317e-06
alternative hypothesis: true difference in means is greater than 0
95 percent confidence interval:
 1.718907      Inf
sample estimates:
mean of x mean of y 
 18.67532  15.96815 

Again we see that p-value is very very small. Thus, the alternate hypothesis must be true. Mean of manual transmission is greater than mean of automatic transmission.

t.test(manual.cty, auto.cty, alternative = "less", var.equal = FALSE)

    Welch Two Sample t-test

data:  manual.cty and auto.cty
t = 4.5375, df = 132.32, p-value = 1
alternative hypothesis: true difference in means is less than 0
95 percent confidence interval:
     -Inf 3.695437
sample estimates:
mean of x mean of y 
 18.67532  15.96815 

Here p-value is 1. We don’t have any evidence supporting the alternate hypothesis.

LS0tDQp0aXRsZTogIkV4cGxvcmF0aW9uIG9mIE1QRyBEYXRhc2V0Ig0KYXV0aG9yOiAiU2hhaWxlc2ggS3VtYXIiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KLS0tDQoNClRoaXMgbm90ZWJvb2sgZXhwbG9yZXMgdGhlIG1wZyBkYXRhc2V0IGF2YWlsYWJsZSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlLiANClRoZSBnb2FscyBvZiB0aGlzIG5vdGVib29rIGFyZToNCg0KKiBHZW5lcmFsIHVuZGVyc3RhbmRpbmcgb2YgbXBnIGRhdGFzZXQNCiogTGVhcm5pbmcgZmVhdHVyZXMgb2YgZ2dwbG90MiBwYWNrYWdlDQoNCmBgYHtyfQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KYGBgDQpUaGlzIGRhdGFzZXQgcHJvdmlkZXMgZnVlbCBlY29ub215IGRhdGEgZnJvbSAxOTk5IGFuZCAyMDA4IGZvciANCjM4IHBvcHVsYXIgbW9kZWxzIG9mIGNhcnMuIFRoZSBkYXRhc2V0IGlzIHNoaXBwZWQgd2l0aCAqZ2dwbG90MiogcGFja2FnZS4NCg0KfCBWYXJpYWJsZSB8IFR5cGUgfCBEZXNjcmlwdGlvbiB8IERldGFpbHMgfCANCnwtLS0tLS0tLS0tfC0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLXwNCnwgbWFudWZhY3R1cmVyIHwgc3RyaW5nIHwgY2FyIG1hbnVmYWN0dXJlciB8IDE1IG1hbnVmYWN0dXJlcnMgfA0KfCBtb2RlbCB8IHN0cmluZyB8bW9kZWwgbmFtZSB8IDM4IG1vZGVscyB8DQp8IGRpc3BsIHwgbnVtZXJpYyB8IGVuZ2luZSBkaXNwbGFjZW1lbnQgaW4gbGl0ZXJzfCAxLjYgLSA3LjAsIG1lZGlhbjogMy4zIHwNCnwgeWVhciB8IGludGVnZXIgfCB5ZWFyIG9mIG1hbnVmYWN0dXJpbmcgfCAxOTk5LCAyMDA4IHwNCnwgY3lsIHwgIHwgbnVtYmVyIG9mIGN5bGluZGVycyB8IDQsIDUsIDYsIDggfCANCnwgdHJhbnN8IHN0cmluZyB8IHR5cGUgb2YgdHJhbnNtaXNzaW9uIHwgYXV0b21hdGljLCBtYW51YWwgKG1hbnkgc3ViIHR5cGVzKSB8DQp8IGRydiB8IHN0cmluZyB8IGRyaXZlIHR5cGUgfCBmLCByLCA0LCBmPWZyb250IHdoZWVsLCByPXJlYXIgd2hlZWwsIDQ9NCB3aGVlbHwNCnwgY3R5IHwgaW50ZWdlciB8IGNpdHkgbWlsZWFnZSB8IG1pbGVzIHBlciBnYWxsb24gfA0KfCBod3kgfCBpbnRlZ2VyIHwgaGlnaHdheSBtaWxlYWdlIHwgbWlsZXMgcGVyIGdhbGxvbiB8DQp8IGZsIHwgc3RyaW5nIHwgZnVlbCB0eXBlIHwgNSBmdWVsIHR5cGVzIChkaWVzZWwsIHBldHJvbCwgZWxlY3RyaWMsIGV0Yy4pIHwNCnwgY2xhc3MgfCBzdHJpbmcgfCB2ZWhpY2xlIGNsYXNzIHwgNyB0eXBlcyAoY29tcGFjdCwgU1VWLCBtaW5pdmFuIGV0Yy4pIHwgDQp8IHx8fA0KDQpEZXNjcmlwdGlvbiBvZiBtcGcgZGF0YXNldA0KYGBge3J9DQpzdHIobXBnKQ0KYGBgDQoNCk51bWJlciBvZiByb3dzIG9yIG9ic2VydmF0aW9ucyBvciByZWNvcmRzDQpgYGB7cn0NCm5yb3cobXBnKQ0KYGBgDQoNCk51bWJlciBvZiBjb2x1bW5zIG9yIHZhcmlhYmxlcw0KYGBge3J9DQpuY29sKG1wZykNCmBgYA0KDQpOYW1lcyBvZiBjb2x1bW5zDQpgYGB7cn0NCmNvbG5hbWVzKG1wZykNCmBgYA0KDQojIyMgTWFudWZhY3R1cmVycw0KYGBge3J9DQp0YWJsZShtcGckbWFudWZhY3R1cmVyKQ0KcXBsb3QobWFudWZhY3R1cmVyLCBkYXRhPW1wZywgZ2VvbT0iYmFyIiwgZmlsbD1tYW51ZmFjdHVyZXIpDQpgYGANCg0KIyMjIFllYXIgb2YgbWFudWZhY3R1cmluZw0KYGBge3J9DQp0YWJsZShtcGckeWVhcikNCnFwbG90KGZhY3Rvcih5ZWFyKSwgZGF0YT1tcGcsIGdlb209ImJhciIsIGZpbGw9ZmFjdG9yKHllYXIpKQ0KYGBgDQoNCg0KIyMjIERpc3BsYWNlbWVudA0KYGBge3J9DQpzdW1tYXJ5KG1wZyRkaXNwbCkNCnFwbG90KGRpc3BsLCBkYXRhPW1wZywgZ2VvbT0iaGlzdG9ncmFtIiwgYmlucz0zMCkNCmJveHBsb3QobXBnJGRpc3BsKQ0KYGBgDQoNCiMjIyBDeWxpbmRlcnMNCmBgYHtyfQ0KdGFibGUobXBnJGN5bCkNCnFwbG90KGN5bCwgZGF0YT1tcGcsIGdlb209ImJhciIsIGZpbGw9ZmFjdG9yKGN5bCkpDQpgYGANCg0KIyMjIFRyYW5zbWlzc2lvbiB0eXBlcw0KYGBge3J9DQp0YWJsZShtcGckdHJhbnMpDQpxcGxvdCh0cmFucywgZGF0YT1tcGcsIGdlb209ImJhciIsIGZpbGw9ZmFjdG9yKHRyYW5zKSkNCmBgYA0KDQojIyMgV2hlZWwgZHJpdmUgdHlwZXMNCmBgYHtyfQ0KdGFibGUobXBnJGRydikNCnFwbG90KGRydiwgZGF0YT1tcGcsIGdlb209ImJhciIsIGZpbGw9ZHJ2KQ0KYGBgDQoNCiMjIyBNaWxlYWdlIGluIGNpdHkNCg0KYGBge3J9DQpzdW1tYXJ5KG1wZyRjdHkpDQpxcGxvdChjdHksIGRhdGE9bXBnLCBnZW9tPSJoaXN0b2dyYW0iLCBiaW5zPTIwKQ0KcXBsb3QoY3R5LCBkYXRhPW1wZywgZ2VvbT0iaGlzdG9ncmFtIiwgYmlucz0zMCkNCnFwbG90KGN0eSwgZGF0YT1tcGcsIGdlb209Imhpc3RvZ3JhbSIsIGJpbnM9NDApDQpgYGANCg0KIyMjIE1pbGVhZ2UgaW4gaGlnaHdheXMNCmBgYHtyfQ0Kc3VtbWFyeShtcGckaHd5KQ0KcXBsb3QoaHd5LCBkYXRhPW1wZywgZ2VvbT0iaGlzdG9ncmFtIiwgYmlucz0yMCkNCnFwbG90KGh3eSwgZGF0YT1tcGcsIGdlb209Imhpc3RvZ3JhbSIsIGJpbnM9MzApDQpxcGxvdChod3ksIGRhdGE9bXBnLCBnZW9tPSJoaXN0b2dyYW0iLCBiaW5zPTQwKQ0KYGBgDQoNCiMjIyBGdWVsIHR5cGVzDQpgYGB7cn0NCnRhYmxlKG1wZyRmbCkNCnFwbG90KGZsLCBkYXRhPW1wZywgZ2VvbT0iYmFyIiwgZmlsbD1mbCkNCmBgYA0KDQojIyMgVmVoaWNsZSBDbGFzcw0KYGBge3J9DQp0YWJsZShtcGckY2xhc3MpDQpxcGxvdChjbGFzcywgZGF0YT1tcGcsIGdlb209ImJhciIsIGZpbGw9Y2xhc3MpDQpgYGANCg0KIyMjIERpc3BsYWNlbWVudCB2cyBoaWdod2F5IGVmZmljaWVuY3kNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpLCBjb2xvcj0nYmx1ZScpDQpgYGANCg0KVGhlIHNhbWUgZ3JhcGggY2FuIGJlIGJ1aWx0IHVzaW5nIHFwbG90IHRvbw0KYGBge3J9DQpxcGxvdChkaXNwbCwgaHd5LCBkYXRhPW1wZywgZ2VvbT0icG9pbnQiLCBjb2xvcj0ncmVkJykNCmBgYA0KDQpMb29raW5nIGF0IHRoaXMgZGF0YSBzZXBhcmF0ZWx5IGZvciBlYWNoIGNsYXNzDQpgYGB7cn0NCnFwbG90KGRpc3BsLCBod3ksIGRhdGE9bXBnLCBnZW9tPSJwb2ludCIsIGNvbG9yPWNsYXNzKQ0KYGBgDQoNCkZpeGluZyB0aGUgY29sb3IgZm9yIGFsbCBwb2ludHMNCmBgYHtyfQ0KcXBsb3QoZGlzcGwsIGh3eSwgZGF0YT1tcGcsIGdlb209InBvaW50IiwgY29sb3I9SSgiYmx1ZSIpKQ0KYGBgDQoNClRoZSBzYW1lIGdyYXBoaWMgdGhyb3VnaCBnZ3Bsb3QNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpLCBjb2xvciA9ICJibHVlIikNCmBgYA0KDQoNClNlcGFyYXRlIGdyYXBocyBmb3IgZWFjaCB2ZWhpY2xlIGNsYXNzOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3I9Y2xhc3MpKSArDQogIGZhY2V0X3dyYXAofiBjbGFzcywgbnJvdyA9IDIpDQpgYGANCg0KQ3JlYXRpbmcgZmFjZXRzIG9uIHRoZSBiYXNpcyBvZiB0d28gdmFyaWFibGVzIDogbnVtYmVyIG9mIGN5bGluZGVycyBhbmQNCnR5cGUgb2YgZHJpdmUNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yPWRydikpICsNCiAgZmFjZXRfZ3JpZChkcnYgfiBjeWwpDQpgYGANCg0KVGhlcmUgYXJlIG5vIGNhcnMgZm9yIHNvbWUgY29tYmluYXRpb25zIG9mIGRyaXZlIGFuZCBjeWxpbmRlcnMuIA0KDQpDcmVhdGluZyBmYWNldHMgdmVydGljYWxseSBvbiBkcml2ZToNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yPWRydikpICsNCiAgZmFjZXRfZ3JpZChkcnYgfiAuKQ0KYGBgDQoNCkNyZWF0aW5nIGZhY2V0cyBob3Jpem9udGFsbHkgb24gY3lsaW5kZXJzOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3I9Y3lsKSkgKw0KICBmYWNldF9ncmlkKC4gfiBjeWwpDQpgYGANCg0KDQpFc3RpbWF0aW5nIGEgc21vb3RoIGN1cnZlIGZvciB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlzcGxhY2VtZW50IGFuZA0KaGlnaHdheSBtaWxlYWdlOg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpDQpgYGANCg0KU2VwYXJhdGUgY3VydmUgZm9yIGVhY2ggdHlwZSBvZiBkcml2ZToNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBsaW5ldHlwZSA9IGRydiwgY29sb3I9ZHJ2KSkNCmBgYA0KDQpHcm91cGluZyBkYXRhOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgZ3JvdXAgPSBkcnYpKQ0KYGBgDQoNCkdyb3VwaW5nIGRhdGEgdXNpbmcgc3BlY2lmaWMgY29sb3I6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsNCiAgZ2VvbV9zbW9vdGgoDQogICAgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yID0gZHJ2KQ0KICApDQpgYGANCg0KSGlkaW5nIHRoZSBsZWdlbmQ6DQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fc21vb3RoKA0KICAgIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRydiksDQogICAgc2hvdy5sZWdlbmQgPSBGQUxTRQ0KICApDQpgYGANCg0KT3ZlcmxheWluZyBhIHNtb290aCBjdXJ2ZSBvbiB0b3Agb2Ygc2NhdHRlciBwbG90Og0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpDQpgYGANCg0KVXNpbmcgYSBjb2xvciBiYXNlZCBncm91cGluZyBmb3IgdGhlIHNjYXR0ZXIgcGxvdCBidXQgYSBjb21tb24NCmN1cnZlIG92ZXJsYXllZCBvbiB0b3Agb2YgaXQ6DQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IGNsYXNzKSkgKyANCiAgZ2VvbV9zbW9vdGgoKQ0KYGBgDQoNCkZpbHRlcmluZyBkYXRhIGZvciBhIHNwZWNpZmljIGdlb206DQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IGNsYXNzKSkgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IGRwbHlyOjpmaWx0ZXIobXBnLCBjbGFzcyA9PSAic3ViY29tcGFjdCIpLCBzZSA9IEZBTFNFKQ0KYGBgDQpUaGUgc2U9RkFMU0Ugc2V0dGluZyByZW1vdmVzIHRoZSBjb25maWRlbmNlIGludGVydmFsIGFyb3VuZCB0aGUNCmVzdGltYXRlZCBjdXJ2ZS4NCg0KR3JvdXBpbmcgZGF0YSBieSBkcml2ZSBhbmQgdGhlbiBkcmF3aW5nIHNjYXR0ZXIgcGxvdCB3aXRoIGVzdGltYXRlZCBjdXJ2ZQ0KZm9yIGVhY2ggZ3JvdXA6DQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBkcnYpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkNCmBgYA0KDQojIyMgRHJpdmUgdnMgQ3lsaW5kZXINCg0KVmlzdWFsaXppbmcgdGhlIGNvbWJpbmF0aW9ucyBvZiBkcml2ZXMgYW5kIGN5bGluZGVycyBhdmFpbGFibGUgaW4gdGhlIGRhdGFzZXQ6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh5ID0gZHJ2LCB4ID0gY3lsLCBjb2xvcj1mYWN0b3IoY3lsKSksIHNpemU9NCkNCmBgYA0KDQojIyMgSXMgYXV0b21hdGljIHRyYW5zbWlzc2lvbiBiZXR0ZXI/IA0KDQpXZSB3aWxsIGNyZWF0ZSBhIGNvcHkgb2Ygb3VyIGRhdGEgZnJhbWUNCmBgYHtyfQ0KbXBnMiA8LSBtcGcNCmBgYA0KDQpXZSB3aWxsIGludHJvZHVjZSBhIG5ldyBjb2x1bW4gZGVmaW5pbmcgd2hldGhlciB0aGUgY2FyDQppcyBhdXRvbWF0aWMgb3IgbWFudWFsDQpgYGB7cn0NCm1wZzIkaXMuYXV0b21hdGljIDwtIHN0YXJ0c1dpdGgobXBnMiR0cmFucywgJ2F1dG8nKQ0KbXBnMiR0cmFuc21pc3Npb24gPC0gaWZlbHNlKG1wZzIkaXMuYXV0b21hdGljLCAnYXV0bycsICdtYW4nKQ0KYGBgDQpMZXQncyB2ZXJpZnkgdGhlIHN0YXRpc3RpY3MNCmBgYHtyfQ0KdGFibGUobXBnMiR0cmFucykNCnRhYmxlKG1wZzIkaXMuYXV0b21hdGljKQ0KYGBgDQoNCg0KTGV0J3MgY29tcGFyZSB0aGUgYm94IHBsb3RzIG9mIGNpdHkgbWlsZWFnZSBmb3IgdGhlIHR3byB0eXBlcw0KYGBge3J9DQpxcGxvdCh0cmFuc21pc3Npb24sIGN0eSwgZGF0YT1tcGcyLCBnZW9tPSdib3hwbG90JywgZmlsbD10cmFuc21pc3Npb24sIGNvbG9yPXRyYW5zbWlzc2lvbikNCmBgYA0KVGhpcyBncmFwaGljIHN1Z2dlc3RzIHRoYXQgdGhlIG1hbnVhbCB0cmFuc21pc3Npb24gaXMgYmV0dGVyDQp0aGFuIGF1dG9tYXRpYyBvbmUuDQoNCkl0IGlzIHRpbWUgZm9yIHVzIHRvIHBlcmZvcm0gYSB0LXRlc3QgdG8gdmVyaWZ5IHRoZSBhY2N1cmFjeS4NCmBgYHtyfQ0KbWFudWFsLmN0eSA8LSBtcGcyJGN0eVshbXBnMiRpcy5hdXRvbWF0aWNdDQphdXRvLmN0eSA8LSBtcGcyJGN0eVttcGcyJGlzLmF1dG9tYXRpY10NCnQudGVzdChtYW51YWwuY3R5LCBhdXRvLmN0eSwgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwgdmFyLmVxdWFsID0gRkFMU0UpDQpgYGANCg0KVGhlIHAtdmFsdWUgaXMgdmVyeSBsb3cuICBJdCBpbmRpY2F0ZXMgc3Ryb25nIGV2aWRlbmNlIGFnYWluc3QgdGhlIG51bGwgaHlwb3RoZXNpcywgc28gd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYW5kIGFjY2VwdCB0aGF0DQptZWFucyBhcmUgbm90IGVxdWFsLg0KDQpMZXQncyBhbHNvIHZlcmlmeSB0aGUgaHlwb3RoZXNpcyB0aGF0IG1lYW4gb2YgbWFudWFsIGlzDQpncmVhdGVyIHRoYW4gbWVhbiBvZiBhdXRvbWF0aWMgdHJhbnNtaXNzaW9uLg0KDQpgYGB7cn0NCnQudGVzdChtYW51YWwuY3R5LCBhdXRvLmN0eSwgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIsIHZhci5lcXVhbCA9IEZBTFNFKQ0KYGBgDQpBZ2FpbiB3ZSBzZWUgdGhhdCBwLXZhbHVlIGlzIHZlcnkgdmVyeSBzbWFsbC4gVGh1cywgdGhlDQphbHRlcm5hdGUgaHlwb3RoZXNpcyBtdXN0IGJlIHRydWUuIE1lYW4gb2YgbWFudWFsDQp0cmFuc21pc3Npb24gaXMgZ3JlYXRlciB0aGFuIG1lYW4gb2YgYXV0b21hdGljIHRyYW5zbWlzc2lvbi4NCg0KYGBge3J9DQp0LnRlc3QobWFudWFsLmN0eSwgYXV0by5jdHksIGFsdGVybmF0aXZlID0gImxlc3MiLCB2YXIuZXF1YWwgPSBGQUxTRSkNCmBgYA0KDQpIZXJlIHAtdmFsdWUgaXMgMS4gV2UgZG9uJ3QgaGF2ZSBhbnkgZXZpZGVuY2Ugc3VwcG9ydGluZyB0aGUNCmFsdGVybmF0ZSBoeXBvdGhlc2lzLg0KDQo=