1 Introduction

The ggplot2 package in R is one of the most popular and versatile tools for creating stunning visualizations. It follows the grammar of graphics concept, allowing users to layer components of a plot in a structured way.

ggplot2 is included as part of the tidyverse package collection. When you install and load the tidyverse (e.g., install.packages("tidyverse") and library(tidyverse)), it automatically includes and loads ggplot2 along with other core packages like dplyr, tidyr, readr, purrr, and more.

However, we can also install and load ggplot2 separately if you only need its functionality for data visualization.

2 Understanding the Grammar of Graphics

The basic structure of a ggplot visualization includes:

Data: The dataset you want to visualize.

Aesthetics (aes): The mappings of variables to visual properties like x, y, color, size, etc.

Geometries (geom): The type of plot, such as points, lines, bars, etc.

3 Creating Your First Plot

Here is a simple scatter plot using the built-in mtcars dataset:

ggplot(data = mtcars, aes(x = wt, y = mpg)) +
  geom_point() + 
  ggtitle("A simple scatter plot with ggplot()")

data = mtcars: Specifies the dataset.

aes(x = wt, y = mpg): Maps the weight (wt) to the x-axis and miles per gallon (mpg) to the y-axis.

geom_point(): Adds points to the plot.

4 Adding Layers and Customizing

You can layer additional components to enhance your plot. For example:

ggplot(data = mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = FALSE) +
  labs(title = "Car Weight vs. MPG", x = "Weight (1000 lbs)", y = "Miles per Gallon", color = "Cylinders") +
  ggtitle("A scatter plot \nwith more graphical layers in ggplot") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

color = factor(cyl): Colors the points based on the number of cylinders.

geom_smooth(): Adds a linear regression line.

labs(): Adds titles and axis labels.

theme_minimal(): Applies a clean theme.


Some Key Elements in them_minima()

  • Background
    • Removes the panel background (no grid-like boxes around the plot).
    • Uses a white background for the plot.
  • Grid Lines
    • Displays only horizontal and vertical grid lines for major axis ticks (no minor grid lines by default).
    • Grid lines are subtle (gray and thin), ensuring they don’t overpower the data visualization.
  • Axes
    • Keep axis lines simple and clean (thin gray lines).
    • Axis ticks are present and minimal.
    • Axis labels (x and y) are included with a clean font.
  • Text
    • Uses a sans-serif font for titles, axis labels, and other text elements by default.
    • Font sizes are balanced for a professional look.
  • Legend
    • Includes a minimalistic legend box.
    • Legend background is removed, showing only the text and symbols.
  • Titles
    • Plot titles and subtitles are included, aligned, and spaced appropriately.
  • Facets
    • Facet strips (titles for faceted plots) have no background color or borders, appearing clean and unobtrusive.


4.1 Common Geometries

Here are some commonly used geometries:

  • Bar Plot
ggplot(data = mtcars, aes(x = factor(cyl))) +
  geom_bar() + 
  ggtitle("A simple gray bar plot")

  • Histogram:
ggplot(data = mtcars, aes(x = mpg)) +
  geom_histogram(binwidth = 5, fill = "blue", color = "black")+
  ggtitle("A simple histogram using ggplot")

  • Line Plot:
ggplot(data = economics, aes(x = date, y = unemploy)) +
  geom_line(color = "red")+
  ggtitle("A simple line plot with ggplot")

5 Faceting

Faceting allows you to create multiple plots based on a factor variable:

ggplot(data = mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  facet_wrap(~ cyl) +
  ggtitle("Multi-panel plot in ggplot")

6 Themes and Customization

You can modify the appearance of your plot using themes:

ggplot(data = mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  theme_classic() +
  ggtitle("Scatter plot using base theme_classic()") + 
  theme(plot.title = element_text(hjust = 0.5))

Here’s an example of using a custom theme in ggplot2:

# Example dataset
data <- data.frame(
  x = c("A", "B", "C", "D"),
  y = c(3, 7, 2, 8)
)

# Create a plot with a custom theme
p <- ggplot(data, aes(x = x, y = y)) +
  geom_col(fill = "steelblue") +
  labs(
    title = "Custom Theme Example",
    x = "Categories",
    y = "Values"
  ) +
  theme_minimal(base_size = 14) +  # Start with a minimal theme
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, color = "darkblue", size = 16),
    axis.text = element_text(color = "black"),
    axis.title = element_text(face = "bold", size = 14),
    panel.grid.major = element_line(color = "gray80"),
    panel.grid.minor = element_blank(), # Hide minor gridlines
    plot.background = element_rect(fill = "lightgray", color = NA),
    panel.background = element_rect(fill = "white"),
    legend.position = "none"  # Hide legend if not needed
  )

# Print the plot
print(p)

Key Features of the Example:

  • Base Theme: The theme_minimal() is used as a starting point.

  • Customizations

    • Title is bold, centered, and colored.
    • Gridlines and axis titles are styled.
    • The background is customized with plot.background and panel.background.
    • Minor gridlines are removed for simplicity.
  • Legend: Hidden as this plot doesn’t need one.

7 Some Base Themes

This example demonstrates how you can tweak a ggplot theme to suit your design preferences. There are other base themes like theme_gray(), theme_classic(), or theme_bw() in ggplot2:

7.1 ggplot1 Default Theme - theme_gray()

In ggplot2, the theme_gray() is the default theme used for plots. It provides a light gray background with white grid lines, offering good readability and focusing attention on the data. Below are the key elements of theme_gray():

  • Background

    • Plot background: Light gray color (#F0F0F0).
    • Panel background: A slightly lighter gray color (#E6E6E6).
  • Grid Lines

    • Major grid lines: White lines for both x and y axes.
    • Minor grid lines: Thinner white lines for both x and y axes (less prominent than major grid lines).
  • Axis Elements

    • Axis text: Black text with default alignment.
    • Axis title: Bold black text for axis labels.
    • Axis ticks: Black tick marks.
  • Title and Subtitle

    • Plot title: Centered at the top, bold black text.
    • Subtitle: Slightly smaller, plain black text, positioned below the title.
  • Legend

    • Background: Transparent.
    • Text: Black.
    • Key: Gray rectangles (matching the panel background).
    • Position: By default, placed inside the plot area.
  • Text Styling

    • Fonts, sizes, and styles (e.g., bold for titles) follow the default ggplot2 settings.
    • Base size: 11 (modifiable via theme_gray(base_size = ...)).
  • Facet Elements

    • Facet background: Transparent or light gray.
    • Facet text: Black, typically aligned to the center.
  • Customizing theme_gray(): To modify or extend its elements, you can:

    • Use it as a starting point: theme_gray(base_size = 12, base_family = "serif").
    • Combine it with theme() for specific changes:
ggplot(data, aes(x, y)) +
  geom_point() +
  theme_gray() +
  theme(panel.grid.major = element_line(color = "blue"))


7.2 theme_classic()

The theme_classic() function in the ggplot2 package of R is used to create a minimalistic theme for plots. It provides a clean and simple aesthetic by removing unnecessary elements and emphasizing the plot’s data. Here are the key elements of theme_classic():

  • Background
    • The plot background is completely white.
    • The panel background (behind the plot area) is also white.
  • Grid Lines
    • Major and minor grid lines are removed, both horizontally and vertically.
    • This gives the plot a cleaner look, focusing on the data points and axes.
  • Axis Lines
    • The x-axis and y-axis lines are retained and drawn in black.
    • These lines enhance the clarity of the axes without extra clutter.
  • Axis Ticks
    • Ticks on the x and y axes are present and remain black.
    • The tick marks are short and simple.
  • Axis Text
    • Labels for both axes (tick labels) are displayed in a standard font and positioned close to the axis lines.
  • Legend
    • The legend box is included but has a minimal appearance.
    • The background is transparent, and unnecessary decorations are removed.
  • Text and Title
    • Plot title, axis labels, and other text elements follow a straightforward and clean typographic style.
    • Text elements can be customized using additional theme() modifications.
  • Overall Simplicity
    • There are no unnecessary borders, shaded regions, or decorative elements, making the data visualization clean and professional.

This theme is ideal for creating publication-ready figures or when a minimalist design is desired. It emphasizes data over styling while retaining the essential visual elements of the plot.

7.3 theme_bw()

The theme_bw() function in ggplot2 is a pre-defined theme in R used to give plots a clean, black-and-white appearance. It focuses on a minimalistic style, typically useful for publications or presentations where color may not be as important. Key elements of theme_bw() include

  • Background
    • The panel background is set to white (panel.background = element_rect(fill = "white")).
    • The plot background (plot.background) is also white, creating a clean look.
  • Gridlines
    • Light grey gridlines (panel.grid.major and panel.grid.minor) are used, which are slightly visible but don’t overwhelm the plot.
  • Axis
    • Axis lines are black (axis.line = element_line(color = "black")), which helps define the boundaries of the plot.
    • Axis text and titles are set to black for clarity.
  • Panel Border
    • The panel has a black border (panel.border = element_rect(color = "black")).
  • Minimalist Aesthetic
    • The overall design removes unnecessary elements, keeping the focus on the data itself.

Legends are placed outside the plot if required and can be styled further, but they are kept simple in theme_bw(). You can further tweak any of these settings using theme() in conjunction with theme_bw() if you need more specific customization.

8 Saving Your Plot

To save your plot, use the ggsave() function:

ggsave("my_plot.png", width = 6, height = 4)

9 Introducing Interactive Features

ggplotly() is a wrapper function from the plotly package in R that allows you to convert a ggplot2 object into an interactive plotly object. It takes a static ggplot and makes it interactive by adding tooltips, zoom, and hover functionalities. This is useful when you want to create more dynamic and engaging visualizations based on ggplot2 syntax but with the interactivity features provided by Plotly. The following is a simple example.

# need to use functions in ggplot2 and plotly
# library(ggplot2)
# library(plotly)

# Create a ggplot
p <- ggplot(mpg, aes(x = displ, y = hwy, color = class)) + 
  geom_point() +
  ggtitle("Interactive scatter plot with color-coding")

# Convert to interactive plotly plot
ggplotly(p)

This code will generate a plot that behaves interactively, allowing you to zoom, pan, and hover over data points to see additional information.

LS0tDQp0aXRsZTogJ0EgVHV0b3JpYWwgb24gZ2dwbG90MiBpbiBSJw0KYXV0aG9yOiAiQ2hlbmcgUGVuZyINCmRhdGU6ICIgV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBkYXJrcmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMTZweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxNHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgDQojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCn0NCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEpICANCmBgYA0KDQpcDQoNClwNCg0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgYGdncGxvdDJgIHBhY2thZ2UgaW4gUiBpcyBvbmUgb2YgdGhlIG1vc3QgcG9wdWxhciBhbmQgdmVyc2F0aWxlIHRvb2xzIGZvciBjcmVhdGluZyBzdHVubmluZyB2aXN1YWxpemF0aW9ucy4gSXQgZm9sbG93cyB0aGUgKipncmFtbWFyIG9mIGdyYXBoaWNzKiogY29uY2VwdCwgYWxsb3dpbmcgdXNlcnMgdG8gbGF5ZXIgY29tcG9uZW50cyBvZiBhIHBsb3QgaW4gYSBzdHJ1Y3R1cmVkIHdheS4gDQoNCmBnZ3Bsb3QyYCBpcyBpbmNsdWRlZCBhcyBwYXJ0IG9mIHRoZSBgdGlkeXZlcnNlYCBwYWNrYWdlIGNvbGxlY3Rpb24uIFdoZW4geW91IGluc3RhbGwgYW5kIGxvYWQgdGhlIGB0aWR5dmVyc2VgIChlLmcuLCBgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIilgIGFuZCBgbGlicmFyeSh0aWR5dmVyc2UpYCksIGl0IGF1dG9tYXRpY2FsbHkgaW5jbHVkZXMgYW5kIGxvYWRzIGBnZ3Bsb3QyYCBhbG9uZyB3aXRoIG90aGVyIGNvcmUgcGFja2FnZXMgbGlrZSBgZHBseXJgLCBgdGlkeXJgLCBgcmVhZHJgLCBgcHVycnJgLCBhbmQgbW9yZS4NCg0KSG93ZXZlciwgd2UgY2FuIGFsc28gaW5zdGFsbCBhbmQgbG9hZCBnZ3Bsb3QyIHNlcGFyYXRlbHkgaWYgeW91IG9ubHkgbmVlZCBpdHMgZnVuY3Rpb25hbGl0eSBmb3IgZGF0YSB2aXN1YWxpemF0aW9uLg0KDQoNCiMgVW5kZXJzdGFuZGluZyB0aGUgR3JhbW1hciBvZiBHcmFwaGljcw0KDQpUaGUgYmFzaWMgc3RydWN0dXJlIG9mIGEgYGdncGxvdGAgdmlzdWFsaXphdGlvbiBpbmNsdWRlczoNCg0KKipEYXRhKio6IFRoZSBkYXRhc2V0IHlvdSB3YW50IHRvIHZpc3VhbGl6ZS4NCg0KKipBZXN0aGV0aWNzIChhZXMpKio6IFRoZSBtYXBwaW5ncyBvZiB2YXJpYWJsZXMgdG8gdmlzdWFsIHByb3BlcnRpZXMgbGlrZSB4LCB5LCBjb2xvciwgc2l6ZSwgZXRjLg0KDQoqKkdlb21ldHJpZXMgKGdlb20pKio6IFRoZSB0eXBlIG9mIHBsb3QsIHN1Y2ggYXMgcG9pbnRzLCBsaW5lcywgYmFycywgZXRjLg0KDQoNCg0KIyBDcmVhdGluZyBZb3VyIEZpcnN0IFBsb3QNCg0KSGVyZSBpcyBhIHNpbXBsZSBzY2F0dGVyIHBsb3QgdXNpbmcgdGhlIGJ1aWx0LWluIG10Y2FycyBkYXRhc2V0Og0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBnZ3RpdGxlKCJBIHNpbXBsZSBzY2F0dGVyIHBsb3Qgd2l0aCBnZ3Bsb3QoKSIpDQpgYGANCg0KYGRhdGEgPSBtdGNhcnNgOiBTcGVjaWZpZXMgdGhlIGRhdGFzZXQuDQoNCmBhZXMoeCA9IHd0LCB5ID0gbXBnKWA6IE1hcHMgdGhlIHdlaWdodCAod3QpIHRvIHRoZSB4LWF4aXMgYW5kIG1pbGVzIHBlciBnYWxsb24gKG1wZykgdG8gdGhlIHktYXhpcy4NCg0KYGdlb21fcG9pbnQoKWA6IEFkZHMgcG9pbnRzIHRvIHRoZSBwbG90Lg0KDQoNCiMgQWRkaW5nIExheWVycyBhbmQgQ3VzdG9taXppbmcNCg0KWW91IGNhbiBsYXllciBhZGRpdGlvbmFsIGNvbXBvbmVudHMgdG8gZW5oYW5jZSB5b3VyIHBsb3QuIEZvciBleGFtcGxlOg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnLCBjb2xvciA9IGZhY3RvcihjeWwpKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJDYXIgV2VpZ2h0IHZzLiBNUEciLCB4ID0gIldlaWdodCAoMTAwMCBsYnMpIiwgeSA9ICJNaWxlcyBwZXIgR2FsbG9uIiwgY29sb3IgPSAiQ3lsaW5kZXJzIikgKw0KICBnZ3RpdGxlKCJBIHNjYXR0ZXIgcGxvdCBcbndpdGggbW9yZSBncmFwaGljYWwgbGF5ZXJzIGluIGdncGxvdCIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQpgY29sb3IgPSBmYWN0b3IoY3lsKWA6IENvbG9ycyB0aGUgcG9pbnRzIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgY3lsaW5kZXJzLg0KDQpgZ2VvbV9zbW9vdGgoKWA6IEFkZHMgYSBsaW5lYXIgcmVncmVzc2lvbiBsaW5lLg0KDQpgbGFicygpYDogQWRkcyB0aXRsZXMgYW5kIGF4aXMgbGFiZWxzLg0KDQpgdGhlbWVfbWluaW1hbCgpYDogQXBwbGllcyBhIGNsZWFuIHRoZW1lLiANCg0KXA0KDQoqKlNvbWUgS2V5IEVsZW1lbnRzIGluIHRoZW1fbWluaW1hKCkqKg0KDQoqICpCYWNrZ3JvdW5kKg0KICArIFJlbW92ZXMgdGhlIHBhbmVsIGJhY2tncm91bmQgKG5vIGdyaWQtbGlrZSBib3hlcyBhcm91bmQgdGhlIHBsb3QpLg0KICArIFVzZXMgYSB3aGl0ZSBiYWNrZ3JvdW5kIGZvciB0aGUgcGxvdC4NCg0KKiAqR3JpZCBMaW5lcyoNCiAgKyBEaXNwbGF5cyBvbmx5IGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGdyaWQgbGluZXMgZm9yIG1ham9yIGF4aXMgdGlja3MgKG5vIG1pbm9yIGdyaWQgbGluZXMgYnkgZGVmYXVsdCkuDQogICsgR3JpZCBsaW5lcyBhcmUgc3VidGxlIChncmF5IGFuZCB0aGluKSwgZW5zdXJpbmcgdGhleSBkb24ndCBvdmVycG93ZXIgdGhlIGRhdGEgdmlzdWFsaXphdGlvbi4NCg0KKiAqQXhlcyoNCiAgKyBLZWVwIGF4aXMgbGluZXMgc2ltcGxlIGFuZCBjbGVhbiAodGhpbiBncmF5IGxpbmVzKS4NCiAgKyBBeGlzIHRpY2tzIGFyZSBwcmVzZW50IGFuZCBtaW5pbWFsLg0KICArIEF4aXMgbGFiZWxzICh4IGFuZCB5KSBhcmUgaW5jbHVkZWQgd2l0aCBhIGNsZWFuIGZvbnQuDQoNCiogKlRleHQqDQogICsgVXNlcyBhIHNhbnMtc2VyaWYgZm9udCBmb3IgdGl0bGVzLCBheGlzIGxhYmVscywgYW5kIG90aGVyIHRleHQgZWxlbWVudHMgYnkgZGVmYXVsdC4NCiAgKyBGb250IHNpemVzIGFyZSBiYWxhbmNlZCBmb3IgYSBwcm9mZXNzaW9uYWwgbG9vay4NCg0KKiAqTGVnZW5kKg0KICArIEluY2x1ZGVzIGEgbWluaW1hbGlzdGljIGxlZ2VuZCBib3guDQogICsgTGVnZW5kIGJhY2tncm91bmQgaXMgcmVtb3ZlZCwgc2hvd2luZyBvbmx5IHRoZSB0ZXh0IGFuZCBzeW1ib2xzLg0KDQoqICpUaXRsZXMqDQogICsgUGxvdCB0aXRsZXMgYW5kIHN1YnRpdGxlcyBhcmUgaW5jbHVkZWQsIGFsaWduZWQsIGFuZCBzcGFjZWQgYXBwcm9wcmlhdGVseS4NCg0KKiAqRmFjZXRzKg0KICArIEZhY2V0IHN0cmlwcyAodGl0bGVzIGZvciBmYWNldGVkIHBsb3RzKSBoYXZlIG5vIGJhY2tncm91bmQgY29sb3Igb3IgYm9yZGVycywgYXBwZWFyaW5nIGNsZWFuIGFuZCB1bm9idHJ1c2l2ZS4NCg0KXA0KDQojIyBDb21tb24gR2VvbWV0cmllcw0KDQpIZXJlIGFyZSBzb21lIGNvbW1vbmx5IHVzZWQgZ2VvbWV0cmllczoNCg0KKiBCYXIgUGxvdCANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywgYWVzKHggPSBmYWN0b3IoY3lsKSkpICsNCiAgZ2VvbV9iYXIoKSArIA0KICBnZ3RpdGxlKCJBIHNpbXBsZSBncmF5IGJhciBwbG90IikNCmBgYA0KDQoqIEhpc3RvZ3JhbToNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywgYWVzKHggPSBtcGcpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJibHVlIiwgY29sb3IgPSAiYmxhY2siKSsNCiAgZ2d0aXRsZSgiQSBzaW1wbGUgaGlzdG9ncmFtIHVzaW5nIGdncGxvdCIpDQpgYGANCg0KKiBMaW5lIFBsb3Q6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBlY29ub21pY3MsIGFlcyh4ID0gZGF0ZSwgeSA9IHVuZW1wbG95KSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAicmVkIikrDQogIGdndGl0bGUoIkEgc2ltcGxlIGxpbmUgcGxvdCB3aXRoIGdncGxvdCIpDQpgYGANCg0KIyAgRmFjZXRpbmcNCg0KRmFjZXRpbmcgYWxsb3dzIHlvdSB0byBjcmVhdGUgbXVsdGlwbGUgcGxvdHMgYmFzZWQgb24gYSBmYWN0b3IgdmFyaWFibGU6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAofiBjeWwpICsNCiAgZ2d0aXRsZSgiTXVsdGktcGFuZWwgcGxvdCBpbiBnZ3Bsb3QiKQ0KYGBgDQoNCg0KIyBUaGVtZXMgYW5kIEN1c3RvbWl6YXRpb24NCg0KWW91IGNhbiBtb2RpZnkgdGhlIGFwcGVhcmFuY2Ugb2YgeW91ciBwbG90IHVzaW5nIHRoZW1lczoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgZ2d0aXRsZSgiU2NhdHRlciBwbG90IHVzaW5nIGJhc2UgdGhlbWVfY2xhc3NpYygpIikgKyANCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KDQpIZXJl4oCZcyBhbiBleGFtcGxlIG9mIHVzaW5nIGEgY3VzdG9tIHRoZW1lIGluIGdncGxvdDI6DQoNCmBgYHtyfQ0KIyBFeGFtcGxlIGRhdGFzZXQNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgeCA9IGMoIkEiLCAiQiIsICJDIiwgIkQiKSwNCiAgeSA9IGMoMywgNywgMiwgOCkNCikNCg0KIyBDcmVhdGUgYSBwbG90IHdpdGggYSBjdXN0b20gdGhlbWUNCnAgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0geCwgeSA9IHkpKSArDQogIGdlb21fY29sKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkN1c3RvbSBUaGVtZSBFeGFtcGxlIiwNCiAgICB4ID0gIkNhdGVnb3JpZXMiLA0KICAgIHkgPSAiVmFsdWVzIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkgKyAgIyBTdGFydCB3aXRoIGEgbWluaW1hbCB0aGVtZQ0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemUgPSAxNiksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTgwIiksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgIyBIaWRlIG1pbm9yIGdyaWRsaW5lcw0KICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImxpZ2h0Z3JheSIsIGNvbG9yID0gTkEpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAgIyBIaWRlIGxlZ2VuZCBpZiBub3QgbmVlZGVkDQogICkNCg0KIyBQcmludCB0aGUgcGxvdA0KcHJpbnQocCkNCmBgYA0KDQoNCktleSBGZWF0dXJlcyBvZiB0aGUgRXhhbXBsZToNCg0KKiBCYXNlIFRoZW1lOiBUaGUgYHRoZW1lX21pbmltYWwoKWAgaXMgdXNlZCBhcyBhIHN0YXJ0aW5nIHBvaW50Lg0KDQoqICoqQ3VzdG9taXphdGlvbnMqKg0KICArIFRpdGxlIGlzIGJvbGQsIGNlbnRlcmVkLCBhbmQgY29sb3JlZC4NCiAgKyBHcmlkbGluZXMgYW5kIGF4aXMgdGl0bGVzIGFyZSBzdHlsZWQuDQogICsgVGhlIGJhY2tncm91bmQgaXMgY3VzdG9taXplZCB3aXRoIGBwbG90LmJhY2tncm91bmRgIGFuZCBgcGFuZWwuYmFja2dyb3VuZGAuDQogICsgTWlub3IgZ3JpZGxpbmVzIGFyZSByZW1vdmVkIGZvciBzaW1wbGljaXR5Lg0KDQoqIExlZ2VuZDogSGlkZGVuIGFzIHRoaXMgcGxvdCBkb2VzbuKAmXQgbmVlZCBvbmUuDQoNCg0KIyBTb21lIEJhc2UgVGhlbWVzDQoNCg0KVGhpcyBleGFtcGxlIGRlbW9uc3RyYXRlcyBob3cgeW91IGNhbiB0d2VhayBhIGBnZ3Bsb3RgIHRoZW1lIHRvIHN1aXQgeW91ciBkZXNpZ24gcHJlZmVyZW5jZXMuIFRoZXJlIGFyZSBvdGhlciBiYXNlIHRoZW1lcyBsaWtlIGB0aGVtZV9ncmF5KClgLCBgdGhlbWVfY2xhc3NpYygpYCwgb3IgYHRoZW1lX2J3KClgIGluIGdncGxvdDI6DQoNCiMjIGBnZ3Bsb3QxYCBEZWZhdWx0IFRoZW1lIC0gYHRoZW1lX2dyYXkoKWANCg0KSW4gZ2dwbG90MiwgdGhlIGB0aGVtZV9ncmF5KClgIGlzIHRoZSBkZWZhdWx0IHRoZW1lIHVzZWQgZm9yIHBsb3RzLiBJdCBwcm92aWRlcyBhIGxpZ2h0IGdyYXkgYmFja2dyb3VuZCB3aXRoIHdoaXRlIGdyaWQgbGluZXMsIG9mZmVyaW5nIGdvb2QgcmVhZGFiaWxpdHkgYW5kIGZvY3VzaW5nIGF0dGVudGlvbiBvbiB0aGUgZGF0YS4gQmVsb3cgYXJlIHRoZSBrZXkgZWxlbWVudHMgb2YgYHRoZW1lX2dyYXkoKWA6DQoNCiogQmFja2dyb3VuZA0KDQogICArIFBsb3QgYmFja2dyb3VuZDogTGlnaHQgZ3JheSBjb2xvciAoI0YwRjBGMCkuDQogICArIFBhbmVsIGJhY2tncm91bmQ6IEEgc2xpZ2h0bHkgbGlnaHRlciBncmF5IGNvbG9yICgjRTZFNkU2KS4NCg0KKiBHcmlkIExpbmVzDQoNCiAgICsgTWFqb3IgZ3JpZCBsaW5lczogV2hpdGUgbGluZXMgZm9yIGJvdGggeCBhbmQgeSBheGVzLg0KICAgKyBNaW5vciBncmlkIGxpbmVzOiBUaGlubmVyIHdoaXRlIGxpbmVzIGZvciBib3RoIHggYW5kIHkgYXhlcyAobGVzcyBwcm9taW5lbnQgdGhhbiBtYWpvciBncmlkIGxpbmVzKS4NCg0KKiBBeGlzIEVsZW1lbnRzDQoNCiAgICsgQXhpcyB0ZXh0OiBCbGFjayB0ZXh0IHdpdGggZGVmYXVsdCBhbGlnbm1lbnQuDQogICArIEF4aXMgdGl0bGU6IEJvbGQgYmxhY2sgdGV4dCBmb3IgYXhpcyBsYWJlbHMuDQogICArIEF4aXMgdGlja3M6IEJsYWNrIHRpY2sgbWFya3MuDQoNCiogVGl0bGUgYW5kIFN1YnRpdGxlDQoNCiAgICsgUGxvdCB0aXRsZTogQ2VudGVyZWQgYXQgdGhlIHRvcCwgYm9sZCBibGFjayB0ZXh0Lg0KICAgKyBTdWJ0aXRsZTogU2xpZ2h0bHkgc21hbGxlciwgcGxhaW4gYmxhY2sgdGV4dCwgcG9zaXRpb25lZCBiZWxvdyB0aGUgdGl0bGUuDQoNCiogTGVnZW5kDQoNCiAgICsgQmFja2dyb3VuZDogVHJhbnNwYXJlbnQuDQogICArIFRleHQ6IEJsYWNrLg0KICAgKyBLZXk6IEdyYXkgcmVjdGFuZ2xlcyAobWF0Y2hpbmcgdGhlIHBhbmVsIGJhY2tncm91bmQpLg0KICAgKyBQb3NpdGlvbjogQnkgZGVmYXVsdCwgcGxhY2VkIGluc2lkZSB0aGUgcGxvdCBhcmVhLg0KDQoqIFRleHQgU3R5bGluZw0KDQogICArIEZvbnRzLCBzaXplcywgYW5kIHN0eWxlcyAoZS5nLiwgYm9sZCBmb3IgdGl0bGVzKSBmb2xsb3cgdGhlIGRlZmF1bHQgZ2dwbG90MiBzZXR0aW5ncy4NCiAgICsgQmFzZSBzaXplOiAxMSAobW9kaWZpYWJsZSB2aWEgYHRoZW1lX2dyYXkoYmFzZV9zaXplID0gLi4uKWApLg0KDQoqIEZhY2V0IEVsZW1lbnRzDQoNCiAgICsgRmFjZXQgYmFja2dyb3VuZDogVHJhbnNwYXJlbnQgb3IgbGlnaHQgZ3JheS4NCiAgICsgRmFjZXQgdGV4dDogQmxhY2ssIHR5cGljYWxseSBhbGlnbmVkIHRvIHRoZSBjZW50ZXIuDQoNCg0KKiAqKkN1c3RvbWl6aW5nIHRoZW1lX2dyYXkoKSoqOiBUbyBtb2RpZnkgb3IgZXh0ZW5kIGl0cyBlbGVtZW50cywgeW91IGNhbjoNCg0KICArIFVzZSBpdCBhcyBhIHN0YXJ0aW5nIHBvaW50OiBgdGhlbWVfZ3JheShiYXNlX3NpemUgPSAxMiwgYmFzZV9mYW1pbHkgPSAic2VyaWYiKWAuDQogICsgQ29tYmluZSBpdCB3aXRoIGB0aGVtZSgpYCBmb3Igc3BlY2lmaWMgY2hhbmdlczoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHgsIHkpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHRoZW1lX2dyYXkoKSArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmx1ZSIpKQ0KYGBgICANCiAgDQogIA0KXA0KDQojIyBgdGhlbWVfY2xhc3NpYygpYA0KDQpUaGUgYHRoZW1lX2NsYXNzaWMoKWAgZnVuY3Rpb24gaW4gdGhlIGdncGxvdDIgcGFja2FnZSBvZiBSIGlzIHVzZWQgdG8gY3JlYXRlIGEgbWluaW1hbGlzdGljIHRoZW1lIGZvciBwbG90cy4gSXQgcHJvdmlkZXMgYSBjbGVhbiBhbmQgc2ltcGxlIGFlc3RoZXRpYyBieSByZW1vdmluZyB1bm5lY2Vzc2FyeSBlbGVtZW50cyBhbmQgZW1waGFzaXppbmcgdGhlIHBsb3QncyBkYXRhLiBIZXJlIGFyZSB0aGUga2V5IGVsZW1lbnRzIG9mIGB0aGVtZV9jbGFzc2ljKClgOg0KDQoqIEJhY2tncm91bmQNCiAgKyBUaGUgcGxvdCBiYWNrZ3JvdW5kIGlzIGNvbXBsZXRlbHkgd2hpdGUuDQogICsgVGhlIHBhbmVsIGJhY2tncm91bmQgKGJlaGluZCB0aGUgcGxvdCBhcmVhKSBpcyBhbHNvIHdoaXRlLg0KDQoqIEdyaWQgTGluZXMNCiAgKyBNYWpvciBhbmQgbWlub3IgZ3JpZCBsaW5lcyBhcmUgcmVtb3ZlZCwgYm90aCBob3Jpem9udGFsbHkgYW5kIHZlcnRpY2FsbHkuDQogICsgVGhpcyBnaXZlcyB0aGUgcGxvdCBhIGNsZWFuZXIgbG9vaywgZm9jdXNpbmcgb24gdGhlIGRhdGEgcG9pbnRzIGFuZCBheGVzLg0KDQoqIEF4aXMgTGluZXMNCiAgKyBUaGUgeC1heGlzIGFuZCB5LWF4aXMgbGluZXMgYXJlIHJldGFpbmVkIGFuZCBkcmF3biBpbiBibGFjay4NCiAgKyBUaGVzZSBsaW5lcyBlbmhhbmNlIHRoZSBjbGFyaXR5IG9mIHRoZSBheGVzIHdpdGhvdXQgZXh0cmEgY2x1dHRlci4NCg0KKiBBeGlzIFRpY2tzDQogICsgVGlja3Mgb24gdGhlIHggYW5kIHkgYXhlcyBhcmUgcHJlc2VudCBhbmQgcmVtYWluIGJsYWNrLg0KICArIFRoZSB0aWNrIG1hcmtzIGFyZSBzaG9ydCBhbmQgc2ltcGxlLg0KDQoqIEF4aXMgVGV4dA0KICArIExhYmVscyBmb3IgYm90aCBheGVzICh0aWNrIGxhYmVscykgYXJlIGRpc3BsYXllZCBpbiBhIHN0YW5kYXJkIGZvbnQgYW5kIHBvc2l0aW9uZWQgY2xvc2UgdG8gdGhlIGF4aXMgbGluZXMuDQoNCiogTGVnZW5kDQogICsgVGhlIGxlZ2VuZCBib3ggaXMgaW5jbHVkZWQgYnV0IGhhcyBhIG1pbmltYWwgYXBwZWFyYW5jZS4NCiAgKyBUaGUgYmFja2dyb3VuZCBpcyB0cmFuc3BhcmVudCwgYW5kIHVubmVjZXNzYXJ5IGRlY29yYXRpb25zIGFyZSByZW1vdmVkLg0KDQoqIFRleHQgYW5kIFRpdGxlDQogICsgUGxvdCB0aXRsZSwgYXhpcyBsYWJlbHMsIGFuZCBvdGhlciB0ZXh0IGVsZW1lbnRzIGZvbGxvdyBhIHN0cmFpZ2h0Zm9yd2FyZCBhbmQgY2xlYW4gdHlwb2dyYXBoaWMgc3R5bGUuDQogICsgVGV4dCBlbGVtZW50cyBjYW4gYmUgY3VzdG9taXplZCB1c2luZyBhZGRpdGlvbmFsIGB0aGVtZSgpYCBtb2RpZmljYXRpb25zLg0KDQoqIE92ZXJhbGwgU2ltcGxpY2l0eQ0KICArIFRoZXJlIGFyZSBubyB1bm5lY2Vzc2FyeSBib3JkZXJzLCBzaGFkZWQgcmVnaW9ucywgb3IgZGVjb3JhdGl2ZSBlbGVtZW50cywgbWFraW5nIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24gY2xlYW4gYW5kIHByb2Zlc3Npb25hbC4NCiAgDQogIA0KVGhpcyB0aGVtZSBpcyBpZGVhbCBmb3IgY3JlYXRpbmcgcHVibGljYXRpb24tcmVhZHkgZmlndXJlcyBvciB3aGVuIGEgbWluaW1hbGlzdCBkZXNpZ24gaXMgZGVzaXJlZC4gSXQgZW1waGFzaXplcyBkYXRhIG92ZXIgc3R5bGluZyB3aGlsZSByZXRhaW5pbmcgdGhlIGVzc2VudGlhbCB2aXN1YWwgZWxlbWVudHMgb2YgdGhlIHBsb3QuDQoNCg0KIyMgYHRoZW1lX2J3KClgDQoNClRoZSBgdGhlbWVfYncoKWAgZnVuY3Rpb24gaW4gZ2dwbG90MiBpcyBhIHByZS1kZWZpbmVkIHRoZW1lIGluIFIgdXNlZCB0byBnaXZlIHBsb3RzIGEgY2xlYW4sIGJsYWNrLWFuZC13aGl0ZSBhcHBlYXJhbmNlLiBJdCBmb2N1c2VzIG9uIGEgbWluaW1hbGlzdGljIHN0eWxlLCB0eXBpY2FsbHkgdXNlZnVsIGZvciBwdWJsaWNhdGlvbnMgb3IgcHJlc2VudGF0aW9ucyB3aGVyZSBjb2xvciBtYXkgbm90IGJlIGFzIGltcG9ydGFudC4gS2V5IGVsZW1lbnRzIG9mIGB0aGVtZV9idygpYCBpbmNsdWRlIA0KDQoqIEJhY2tncm91bmQgDQogICsgVGhlIHBhbmVsIGJhY2tncm91bmQgaXMgc2V0IHRvIHdoaXRlIChgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIilgKS4NCiAgKyBUaGUgcGxvdCBiYWNrZ3JvdW5kIChgcGxvdC5iYWNrZ3JvdW5kYCkgaXMgYWxzbyB3aGl0ZSwgY3JlYXRpbmcgYSBjbGVhbiBsb29rLg0KDQoqIEdyaWRsaW5lcyANCiAgKyBMaWdodCBncmV5IGdyaWRsaW5lcyAoYHBhbmVsLmdyaWQubWFqb3JgIGFuZCBgcGFuZWwuZ3JpZC5taW5vcmApIGFyZSB1c2VkLCB3aGljaCBhcmUgc2xpZ2h0bHkgdmlzaWJsZSBidXQgZG9u4oCZdCBvdmVyd2hlbG0gdGhlIHBsb3QuDQoNCiogQXhpcw0KICArIEF4aXMgbGluZXMgYXJlIGJsYWNrIChgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIilgKSwgd2hpY2ggaGVscHMgZGVmaW5lIHRoZSBib3VuZGFyaWVzIG9mIHRoZSBwbG90Lg0KICArIEF4aXMgdGV4dCBhbmQgdGl0bGVzIGFyZSBzZXQgdG8gYmxhY2sgZm9yIGNsYXJpdHkuDQoNCiogUGFuZWwgQm9yZGVyDQogICsgVGhlIHBhbmVsIGhhcyBhIGJsYWNrIGJvcmRlciAoYHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJibGFjayIpYCkuDQoNCiogTWluaW1hbGlzdCBBZXN0aGV0aWMgDQogICsgVGhlIG92ZXJhbGwgZGVzaWduIHJlbW92ZXMgdW5uZWNlc3NhcnkgZWxlbWVudHMsIGtlZXBpbmcgdGhlIGZvY3VzIG9uIHRoZSBkYXRhIGl0c2VsZi4NCg0KTGVnZW5kcyBhcmUgcGxhY2VkIG91dHNpZGUgdGhlIHBsb3QgaWYgcmVxdWlyZWQgYW5kIGNhbiBiZSBzdHlsZWQgZnVydGhlciwgYnV0IHRoZXkgYXJlIGtlcHQgc2ltcGxlIGluIGB0aGVtZV9idygpYC4NCllvdSBjYW4gZnVydGhlciB0d2VhayBhbnkgb2YgdGhlc2Ugc2V0dGluZ3MgdXNpbmcgYHRoZW1lKClgIGluIGNvbmp1bmN0aW9uIHdpdGggYHRoZW1lX2J3KClgIGlmIHlvdSBuZWVkIG1vcmUgc3BlY2lmaWMgY3VzdG9taXphdGlvbi4NCiAgDQoNCiMgU2F2aW5nIFlvdXIgUGxvdA0KDQpUbyBzYXZlIHlvdXIgcGxvdCwgdXNlIHRoZSBgZ2dzYXZlKClgIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCmdnc2F2ZSgibXlfcGxvdC5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpDQpgYGANCg0KDQojIEludHJvZHVjaW5nIEludGVyYWN0aXZlIEZlYXR1cmVzDQoNCmBnZ3Bsb3RseSgpYCBpcyBhIHdyYXBwZXIgZnVuY3Rpb24gZnJvbSB0aGUgYHBsb3RseSBwYWNrYWdlYCBpbiBSIHRoYXQgYWxsb3dzIHlvdSB0byBjb252ZXJ0IGEgYGdncGxvdDJgIG9iamVjdCBpbnRvIGFuIGludGVyYWN0aXZlIGBwbG90bHlgIG9iamVjdC4gSXQgdGFrZXMgYSBzdGF0aWMgZ2dwbG90IGFuZCBtYWtlcyBpdCBpbnRlcmFjdGl2ZSBieSBhZGRpbmcgdG9vbHRpcHMsIHpvb20sIGFuZCBob3ZlciBmdW5jdGlvbmFsaXRpZXMuIFRoaXMgaXMgdXNlZnVsIHdoZW4geW91IHdhbnQgdG8gY3JlYXRlIG1vcmUgZHluYW1pYyBhbmQgZW5nYWdpbmcgdmlzdWFsaXphdGlvbnMgYmFzZWQgb24gYGdncGxvdDIgc3ludGF4YCBidXQgd2l0aCB0aGUgaW50ZXJhY3Rpdml0eSBmZWF0dXJlcyBwcm92aWRlZCBieSBgUGxvdGx5YC4gVGhlIGZvbGxvd2luZyBpcyBhIHNpbXBsZSBleGFtcGxlLg0KDQpgYGB7cn0NCiMgbmVlZCB0byB1c2UgZnVuY3Rpb25zIGluIGdncGxvdDIgYW5kIHBsb3RseQ0KIyBsaWJyYXJ5KGdncGxvdDIpDQojIGxpYnJhcnkocGxvdGx5KQ0KDQojIENyZWF0ZSBhIGdncGxvdA0KcCA8LSBnZ3Bsb3QobXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGNsYXNzKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2d0aXRsZSgiSW50ZXJhY3RpdmUgc2NhdHRlciBwbG90IHdpdGggY29sb3ItY29kaW5nIikNCg0KIyBDb252ZXJ0IHRvIGludGVyYWN0aXZlIHBsb3RseSBwbG90DQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KVGhpcyBjb2RlIHdpbGwgZ2VuZXJhdGUgYSBwbG90IHRoYXQgYmVoYXZlcyBpbnRlcmFjdGl2ZWx5LCBhbGxvd2luZyB5b3UgdG8gem9vbSwgcGFuLCBhbmQgaG92ZXIgb3ZlciBkYXRhIHBvaW50cyB0byBzZWUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbi4NCg0KDQoNCg0K