Components of the
layered grammar of graphics
Layers are used to create the objects on a plot.
They are defined by five basic parts:
- Data (What dataset/spreadsheet am I using?)
- Mapping (What does each column do in my graph?)
- Statistical transformation (stat) (Do I have count something
first?)
- Geometric object (geom) (What shape, colour, size…do I want?)
- Position adjustment (position) (Where do I want it on the
graph?)
Data
We will use “real world” data. Let’s use the penguins
dataset in the palmerpenguins
package. Run
?penguins
in the console to get more information about this
dataset.
Head
head(penguins)
## # A tibble: 6 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
## <fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
## 1 Adelie Torgersen 39.1 18.7 181 3750 male 2007
## 2 Adelie Torgersen 39.5 17.4 186 3800 female 2007
## 3 Adelie Torgersen 40.3 18 195 3250 female 2007
## 4 Adelie Torgersen NA NA NA NA <NA> 2007
## 5 Adelie Torgersen 36.7 19.3 193 3450 female 2007
## 6 Adelie Torgersen 39.3 20.6 190 3650 male 2007
Tail
tail(penguins)
## # A tibble: 6 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
## <fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
## 1 Chinstrap Dream 45.7 17 195 3650 female 2009
## 2 Chinstrap Dream 55.8 19.8 207 4000 male 2009
## 3 Chinstrap Dream 43.5 18.1 202 3400 female 2009
## 4 Chinstrap Dream 49.6 18.2 193 3775 male 2009
## 5 Chinstrap Dream 50.8 19 210 4100 male 2009
## 6 Chinstrap Dream 50.2 18.7 198 3775 female 2009
Dim
dim(penguins)
## [1] 344 8
So we know what our data looks like. We pass this data to
ggplot
use to plot as follows: in R this creates an empty
graph sheet!! Because we have not (yet) declared the geometric shapes we
want to use to plot our information.
ggplot(data = penguins) # Creates an empty graphsheet, ready for plotting!!
Mapping
Now that we have told R what data to use, we need to state what
variables to plot and how.
Aesthetic Mapping defines how the variables are
applied to the plot, i.e. we take a variable from the data and
“metaphorize” it into a geometric feature. We can map variables
metaphorically to a variety of geometric things: coordinate, length,
height, size, shape, colour, alpha(how dark?)….
The syntax uses:
aes(some_geometric_thing = some_variable)
Remember variable = column.
So if we were graphing information from penguins
, we
might map a penguin’s flipper_length_mm
column to the \(x\)
position, and the body_mass_g
column to
the \(y\) position.
Mapping Example-1
We can try another example of aesthetic mapping with the same
dataset:
Plot-1a
ggplot(data = penguins)
Plot-1b
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Plot-1c
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g,
fill = island) # color aesthetic = another variable
)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Mapping Example-2
We can try another example of aesthetic mapping with the same
dataset:
Plot-2a
ggplot(data = penguins)
Plot-2b
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Plot-2c
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g,
fill = island) # color aesthetic = another variable
)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Mapping Example-3
We can try another example of aesthetic mapping with the same
dataset:
Plot-3a
ggplot(data = penguins)
Plot-3b
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Plot-3c
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g,
fill = island) # color aesthetic = another variable
)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Mapping Example-4
We can try another example of aesthetic mapping with the same
dataset:
Plot-4a
ggplot(data = penguins)
Plot-4b
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Plot-4c
ggplot(penguins) +
# Plot geom = histogram. So we need a quantity on the x
geom_histogram(
aes(x = body_mass_g,
fill = island) # color aesthetic = another variable
)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 2 rows containing non-finite values (stat_bin).
Geometric objects
Geometric objects (geoms) control the type
of plot you create. Geoms are classified by their dimensionality:
- 0 dimensions - point, text
- 1 dimension - path, line
- 2 dimensions - polygon, interval
Each geom can only display certain aesthetics or
visual attributes of the geom. For example, a point geom has position,
color, shape, and size aesthetics.
We can also stack up geoms on top of one another to add layers to the
graph.
Plot1
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_line()
## Warning: Removed 2 row(s) containing missing values (geom_path).
Plot2
ggplot(data = penguins) +
geom_line(aes(x = bill_length_mm,
y = body_mass_g))
## Warning: Removed 2 row(s) containing missing values (geom_path).
Plot3
ggplot(data = penguins) +
geom_point(aes(x = bill_length_mm,
y = body_mass_g,
color = island,
shape = species)) +
ggtitle("A point geom with position and color and shape aesthetics")
## Warning: Removed 2 rows containing missing values (geom_point).
ggplot(data = penguins,
aes(x = species)) + # x position => ?
# No need to type "mapping"...
geom_bar() + # Where does the height come from?
ggtitle("A bar geom with position and height aesthetics")
ggplot(data = penguins, aes(x = species)) +
geom_bar() +
ggtitle("A bar geom with position and height aesthetics")
- Position determines the starting location (origin) of each bar
- Height determines how tall to draw the bar. Here the height is based
on the number of observations in the dataset for each possible number of
cylinders.
Position adjustment
Sometimes with dense data we need to adjust the position of elements
on the plot, otherwise data points might obscure one another. Bar plots
frequently stack or dodge the bars to
avoid overlap:
count(x = penguins, species, island) %>%
ggplot(mapping = aes(x = species, y = n, fill = island)) +
geom_bar(stat = "identity") +
ggtitle(label = "A stacked bar chart")
count(x = penguins, species, island) %>%
ggplot(mapping = aes(x = species, y = n, fill = island)) +
geom_bar(stat = "identity", position = "dodge") +
ggtitle(label = "A dodged bar chart")
penguins %>%
ggplot(mapping = aes(x = species, fill = island)) +
geom_bar() +
ggtitle(label = "A stacked bar chart")
penguins %>%
ggplot(mapping = aes(x = species, fill = island)) +
geom_bar(position = "dodge") +
ggtitle(label = "A dodged bar chart")
Sometimes scatterplots with few unique \(x\) and \(y\) values are jittered
(random noise is added) to reduce overplotting.
ggplot(data = penguins,
mapping = aes(x = species,
y = body_mass_g)) +
geom_point() +
ggtitle("A point geom with obscured data points")
## Warning: Removed 2 rows containing missing values (geom_point).
ggplot(data = penguins,
mapping = aes(x = species,
y = body_mass_g)) +
geom_jitter() +
ggtitle("A point geom with jittered data points")
## Warning: Removed 2 rows containing missing values (geom_point).
Scale
A scale controls how data is mapped to aesthetic
attributes, so we need one scale for every aesthetic property employed
in a layer. For example, this graph defines a scale for color:
ggplot(data = penguins,
mapping = aes(x = bill_depth_mm,
y = bill_length_mm,
color = species)) +
geom_point()
## Warning: Removed 2 rows containing missing values (geom_point).
The scale can be changed to use a different color palette:
ggplot(data = penguins,
mapping = aes(x = bill_length_mm,
y = body_mass_g,
color = species)) +
geom_point() +
scale_color_brewer(palette = "Dark2",direction = -1)
## Warning: Removed 2 rows containing missing values (geom_point).
Now we are using a different palette, but the scale is still
consistent: all Adelie penguins utilize the same color, whereas
Chinstrap use a new color but each Adelie still uses the same,
consistent color.
Coordinate system
A coordinate system (coord) maps the
position of objects onto the plane of the plot, and controls how the
axes and grid lines are drawn. Plots typically use two coordinates
(\(x, y\)), but could use any number of
coordinates. Most plots are drawn using the Cartesian
coordinate system:
x1 <- c(1, 10)
y1 <- c(1, 5)
p <- qplot(x = x1, y = y1, geom = "point", xlab = NULL, ylab = NULL) +
theme_bw()
p +
ggtitle(label = "Cartesian coordinate system")
ggplot(penguins, aes(flipper_length_mm, body_mass_g)) +
geom_point() +
coord_polar()
## Warning: Removed 2 rows containing missing values (geom_point).
This system requires a fixed and equal spacing between values on the
axes. That is, the graph draws the same distance between 1 and 2 as it
does between 5 and 6. The graph could be drawn using a semi-log
coordinate system which logarithmically compresses the
distance on an axis:
p +
coord_trans(y = "log10") +
ggtitle(label = "Semi-log coordinate system")
Or could even be drawn using polar
coordinates:
p +
coord_polar() +
ggtitle(label = "Polar coordinate system")
Faceting
Faceting can be used to split the data up into
subsets of the entire dataset. This is a powerful tool when
investigating whether patterns are the same or different across
conditions, and allows the subsets to be visualized on the same plot
(known as conditioned or trellis
plots). The faceting specification describes which variables should be
used to split up the data, and how they should be arranged.
ggplot(data = penguins,
mapping = aes(x = bill_length_mm,
y = body_mass_g)) +
geom_point() +
facet_wrap(~ island)
## Warning: Removed 2 rows containing missing values (geom_point).
ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = body_mass_g, color = sex)) +
geom_point() +
facet_grid(species ~ island, scales = "free_y")
## Warning: Removed 2 rows containing missing values (geom_point).
# Ria's explanation: This code did not work becasue....
Defaults
Rather than explicitly declaring each component of a layered graphic
(which will use more code and introduces opportunities for errors), we
can establish intelligent defaults for specific geoms and scales. For
instance, whenever we want to use a bar geom, we can default to using a
stat that counts the number of observations in each group of our
variable in the \(x\) position.
Consider the following scenario: you wish to generate a scatterplot
visualizing the relationship between penguins’ bill_length and their
body_mass. With no defaults, the code to generate this graph is:
ggplot() +
layer(
data = penguins,
mapping = aes(x = bill_length_mm,
y = body_mass_g),
geom = "point",
stat = "identity",
position = "identity"
) +
scale_x_continuous() +
scale_y_continuous() +
coord_cartesian()
## Warning: Removed 2 rows containing missing values (geom_point).
The above code:
Creates a new plot object (ggplot
)
Adds a layer (layer
)
- Specifies the data (
penguins
)
- Maps engine bill length to the \(x\) position and body mass to the \(y\) position (
mapping
)
- Uses the point geometric transformation
(
geom = "point"
)
- Implements an identity transformation and position
(
stat = "identity"
and
position = "identity"
)
Establishes two continuous position scales
(scale_x_continuous
and
scale_y_continuous
)
Declares a cartesian coordinate system
(coord_cartesian
)
How can we simplify this using intelligent defaults?
We only need to specify one geom and stat, since each geom has a
default stat.
Cartesian coordinate systems are most commonly used, so it should
be the default.
Default scales can be added based on the aesthetic and type of
variables.
- Continuous values are transformed with a linear scaling.
- Discrete values are mapped to integers.
- Scales for aesthetics such as color, fill, and size can also be
intelligently defaulted.
Using these defaults, we can rewrite the above code as:
ggplot() +
geom_point(data = penguins,
mapping = aes(x = bill_length_mm,
y = body_mass_g))
## Warning: Removed 2 rows containing missing values (geom_point).
This generates the exact same plot, but uses fewer lines of code.
Because multiple layers can use the same components (data, mapping,
etc.), we can also specify that information in the ggplot()
function rather than in the layer()
function:
ggplot(data = penguins,
mapping = aes(x = bill_length_mm,
y = body_mass_g)) +
geom_point()
## Warning: Removed 2 rows containing missing values (geom_point).
And as we will learn, function arguments in R use specific ordering,
so we can omit the explicit call to data
and
mapping
:
ggplot(penguins, aes(bill_length_mm, body_mass_g)) +
geom_point()
## Warning: Removed 2 rows containing missing values (geom_point).
LS0tDQp0aXRsZTogIlRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzIg0KYXV0aG9yOiAiQXJ2aW5kIFZlbmthdGFkcmkiDQpkYXRlOiAwNi9KdWx5LzIwMjENCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgdG9jX2RlcHRoOiAyDQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KYWJzdHJhY3Q6IFBhcnQgb2YgdGhlIGBSIGZvciBBcnRpc3RzIGFuZCBEZXNpZ25lcnNgIGF0IHRoZSBTY2hvb2wgb2YgRm91bmRhdGlvbiBTdHVkaWVzLCBTcmlzaHRpIE1hbmlwYWwgSW5zdGl0dXRlIG9mIEFydCwgRGVzaWduLCBhbmQgVGVjaG5vbG9neSwgQmFuZ2Fsb3JlLg0KLS0tDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoaXMgUk1hcmtkb3duIGRvY3VtZW50IGlzIHBhcnQgb2YgdGhlIEdlbmVyaWMgU2tpbGxzIENvbXBvbmVudCAgKEdTSykgb2YgdGhlIENvdXJzZSBvZiB0aGUgIEZvdW5kYXRpb24gU3R1ZGllcyBQcm9ncmFtbWUgYXQgU3Jpc2h0aSBNYW5pcGFsIEluc3RpdHV0ZSBvZiBBcnQsIERlc2lnbiwgYW5kIFRlY2hub2xvZ3ksIEJhbmdhbG9yZSBJbmRpYS4gVGhlIG1hdGVyaWFsIGlzIGJhc2VkIG9uICpBIExheWVyZWQgR3JhbW1hciBvZiBHcmFwaGljcyogYnkgSGFkbGV5IFdpY2toYW0uIFRoZSBjb3Vyc2UgaXMgbWVhbnQgZm9yIEZpcnN0IFllYXIgc3R1ZGVudHMgcHVyc3VpbmcgYSBEZWdyZWUgaW4gQXJ0IGFuZCBEZXNpZ24uIA0KDQpUaGUgaW50ZW50IG9mIHRoaXMgR1NLIHBhcnQgaXMgdG8gYnVpbGQgU2tpbGwgaW4gY29kaW5nIGluIFIsIGFuZCBhbHNvIGFwcHJlY2lhdGUgUiBhcyBhIHdheSB0byBtZXRhcGhvcmljYWxseSB2aXN1YWxpemUgaW5mb3JtYXRpb24gb2YgdmFyaW91cyBraW5kcywgdXNpbmcgcHJlZG9taW5hbnRseSBnZW9tZXRyaWMgZmlndXJlcyBhbmQgc3RydWN0dXJlcy4NCg0KQWxsIFJNYXJrZG93biBmaWxlcyBjb21iaW5lIGNvZGUsIHRleHQsIHdlYi1pbWFnZXMsIGFuZCBmaWd1cmVzIGRldmVsb3BlZCB1c2luZyBjb2RlLiBFdmVyeXRoaW5nIGlzIHRleHQ7IGNvZGUgY2h1bmtzIGFyZSBlbmNsb3NlZCBpbiAqKmZlbmNlcyoqIChgYGApDQoNCg0KIyBHb2Fscw0KDQpBdCB0aGUgZW5kIG9mIHRoaXMgTGFiIHNlc3Npb24sIHdlIHNob3VsZDoNCi0ga25vdyB0aGUgdHlwZXMgYW5kIHN0cnVjdHVyZXMgb2YgdGlkeSBkYXRhIGFuZCBiZSBhYmxlIHRvIHdvcmsgd2l0aCB0aGVtDQotIGJlIGFibGUgdG8gY3JlYXRlIGRhdGEgdmlzdWFsaXphdGlvbnMgdXNpbmcgYGdncGxvdGANCi0gVW5kZXJzdGFuZCAqYWVzdGhldGljcyogYW5kICpzY2FsZXMqIGluIGBnZ3Bsb3QNCg0KDQojIFBlZGFnb2dpY2FsIE5vdGUNCg0KVGhlIG1ldGhvZCBmb2xsb3dlZCB3aWxsIGJlIGJhc2VkIG9uDQpbUFJJTU1dKGh0dHBzOi8vYmxvZ3Mua2NsLmFjLnVrL2NzZXIvMjAxNy8wOS8wMS9wcmltbS1hLXN0cnVjdHVyZWQtYXBwcm9hY2gtdG8tdGVhY2hpbmctcHJvZ3JhbW1pbmcvKToNCg0KLSAgICoqUFJFRElDVCoqIEluc3BlY3QgdGhlIGNvZGUgYW5kIGd1ZXNzIGF0IHdoYXQgdGhlIGNvZGUgbWlnaHQgZG8sDQogICAgKip3cml0ZSBwcmVkaWN0aW9ucyoqDQotICAgKipSVU4qKiB0aGUgY29kZSBwcm92aWRlZCBhbmQgY2hlY2sgd2hhdCBoYXBwZW5zDQotICAgKipJTkZFUioqIHdoYXQgdGhlIGBwYXJhbWV0ZXJzYCBvZiB0aGUgY29kZSBkbyBhbmQgKip3cml0ZSBjb21tZW50cyB0byBleHBsYWluKiouIFdoYXQgYmVsbHMgYW5kIHdoaXN0bGVzIGNhbiB5b3Ugc2VlPw0KLSAgICoqTU9ESUZZKiogdGhlIGBwYXJhbWV0ZXJzYCBjb2RlIHByb3ZpZGVkIHRvIHVuZGVyc3RhbmQgdGhlDQogICAgYG9wdGlvbnNgIGF2YWlsYWJsZS4gKipXcml0ZSBjb21tZW50cyoqIHRvIHNob3cgd2hhdCB5b3UgaGF2ZSBhaW1lZCBmb3IgYW5kIGFjaGlldmVkLg0KLSAgICoqTUFLRSoqIDogdGFrZSBhbiBpZGVhL2NvbmNlcHQgb2YgeW91ciBvd24sIGFuZCBncmFwaCBpdC4NCg0KDQojIyBTZXQgVXANCg0KVGhlIGBzZXR1cGAgY29kZSAqKmNodW5rKiogYmVsb3cgYnJpbmdzIGludG8gb3VyIGNvZGluZyBzZXNzaW9uICoqUiBwYWNrYWdlcyoqIHRoYXQgcHJvdmlkZSBzcGVjaWZpYyBjb21wdXRhdGlvbmFsIGFiaWxpdGllcyBhbmQgYWxzbyAqKmRhdGFzZXRzKiogd2hpY2ggd2UgY2FuIHVzZS4gDQoNClRvIHJlaXRlcmF0ZTogUGFja2FnZXMgYW5kIGRhdGFzZXRzIGFyZSAqKm5vdCoqIHRoZSBzYW1lIHRoaW5nICEhIFBhY2thZ2VzIGFyZSAoc21hbGwpIGNvbGxlY3Rpb25zIG9mIHByb2dyYW1zLiBEYXRhc2V0cyBhcmUganVzdC4uLi5pbmZvcm1hdGlvbi4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFKQ0Kb3B0aW9ucyhkaWdpdHMgPSAzKQ0KDQojIEhlcmUgd2Ugd2lsbCBpbmNsdWRlICoqcGFja2FnZXMqKiB0aGF0IGFsbG93IHVzIHRvIGNvZGUgaW4gc3BlY2lmaWMgd2F5LiANCiMgVXNpbmcgdGhlIGxpYnJhcnkoKSBjb21tYW5kLg0KIyBTb21lIHBhY2thZ2VzIGFsc28gY29udGFpbiBkYXRhc2V0cyB0aGF0IHdlIGNhbiB1c2UNCg0KDQojIFByb2dyYW1zIHRvIG1lc3Mgd2l0aCBkYXRhLCBhbmQgdG8gcGxvdCBpdC4NCiMgQWxzbyBoYXMgc29tZSBkZW1vIGRhdGFzZXRzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgDQoNCiMgRGF0YSBmcm9tIEVjb2xvZ3kNCiMgQmVjb21pbmcgdGhlIGBsb3JlbSBpcHN1bWAgZGF0YXNldCBpbiBSISENCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQoNCiMgQSBjb2xsZWN0aW9uIG9mIGhpc3RvcmljYWwgZGF0YXNldHMNCmxpYnJhcnkoSGlzdERhdGEpDQoNCiMgRm9yIHBlZGFnb2dpY2FsIHJlYXNvbnMNCmxpYnJhcnkoZmxhaXIpDQoNCmBgYA0KDQoNCg0KIyBBIFRlYXNlciBmcm9tIEpvaG4gU25vdw0KDQpgYGB7ciB0ZWFzZXIsZWNobz1GQUxTRX0NCg0KIyBTbm93TWFwKHBvbHlnb25zID0gVFJVRSkNCiMgV3JpdGUgc29tZSBjb21tZW50cyBvbiB0aGlzIGNvZGUgYW5kIHdoYXQgaXQgc2VlbXMgdG8gYmUgY3JlYXRpbmcNCiMgQXJlIHRoZXJlICJsYXllcnMiIGluIHRoaXMgdmlzdWFsaXphdGlvbj8NCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHNmaGVhZGVycykNCmxpYnJhcnkoZ2dmb3JtdWxhKQ0KDQpkYXRhKFNub3cuZGVhdGhzKQ0KZGF0YShTbm93LnB1bXBzKQ0KZGF0YShTbm93LnN0cmVldHMpDQpkYXRhKFNub3cucG9seWdvbnMpDQpkYXRhKFNub3cuZGF0ZXMpDQoNCiMjIENvbnZlcnQgdG8gc3BhdGlhbCBmb3JtYXQgdXNpbmcgc2ZoZWFkZXJzDQpzbm93X3N0cmVldHNfc2YgPC0gU25vdy5zdHJlZXRzICU+JSANCiAgc2ZoZWFkZXJzOjpzZl9saW5lc3RyaW5nKC4sIGxpbmVzdHJpbmdfaWQgPSAic3RyZWV0Iix4ID0gIngiLCB5ID0gInkiKQ0KDQojIyMjIw0Kc25vd19wb2x5Z29uc19zZiA8LSBTbm93LnBvbHlnb25zICU+JSANCiAgbWFwX2RmKC5mID0gfiBzZmhlYWRlcnM6OnNmX2xpbmVzdHJpbmcoLngpLCBjKHggPSAueCQieCIsIHkgPSAueCQieSIsIGtlZXAgPSBUUlVFKSkNCg0KIyMjIyMNCnNub3dfcHVtcHNfc2YgPC0gU25vdy5wdW1wcyAlPiUgc2ZoZWFkZXJzOjpzZl9wb2ludCh4ID0gIngiLCB5ID0gInkiLCBrZWVwID0gVFJVRSkNCg0KIyMjIw0Kc25vd19kZWF0aHNfc2YgPC0gU25vdy5kZWF0aHMyICU+JSBzZmhlYWRlcnM6OnNmX3BvaW50KHggPSAieCIsIHkgPSAieSIsIGtlZXAgPSBUUlVFKQ0KDQojIyMgTm93IHRvIHBsb3QgaXQNCg0KZ2dwbG90KCkgKyANCiAgDQogICAgDQogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoZGF0YSA9IFNub3cuZGVhdGhzMiwgYWVzKHggPSB4LCB5ID0geSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIHNob3cubGVnZW5kID0gRkFMU0UsIGJpbnMgPSA5LCBhbHBoYSA9IDAuNSkgKyANCiAgDQogIGdlb21fc2YoZGF0YSA9IHNub3dfcG9seWdvbnNfc2YsIGNvbG9yID0gImJyb3duIixmaWxsID0gIndoaXRlIiwNCiAgICAgICAgICBsaW5ld2lkdGggPSAxKSArDQogIA0KICBnZW9tX3NmKGRhdGEgPSBzbm93X3N0cmVldHNfc2YsIGNvbG9yID0gImdyZXkiKSArDQogIA0KICBnZW9tX3NmKGRhdGEgPSBzbm93X3B1bXBzX3NmLCBmaWxsID0gImJsdWUiLCBjb2xvciA9ICJibHVlIiwNCiAgICAgICAgICBzaGFwZSA9IDE3LCBzaXplID0gNikgKw0KICANCiAgZ2VvbV9zZihkYXRhID0gc25vd19kZWF0aHNfc2YsIHNoYXBlID0gMjIsIGZpbGwgPSAicmVkIiwgDQogICAgICAgICAgICBjb2xvdXIgPSAicmVkIiwgc2l6ZSA9IDEpICsgDQogIA0KICBnZW9tX3NmX3RleHQoZGF0YSA9IHNub3dfcHVtcHNfc2YsYWVzKGxhYmVsID0gbGFiZWwpLA0KICAgICAgICAgICAgICAgbnVkZ2VfeCA9IDAuNSwgbnVkZ2VfeSA9IC0wLjc1KSArIA0KICANCg0KICANCiAgIyBzdGF0X2RlbnNpdHlfMmRfZmlsbGVkKGRhdGEgPSBTbm93LmRlYXRoczIsIGFlcyh4ID0geCwgeSA9IHkpLCBiaW5zID0gOCwgY29udG91cl92YXIgPSAibmRlbnNpdHkiKSArDQogIA0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkdyZWVucyIsIGRpcmVjdGlvbiA9IDEsdHlwZSA9ICJxdWFsIikgKyANCiAgdGhlbWVfdm9pZCgpDQoNCg0KYGBgDQoNCg0KIyBSZXZpZXcgb2YgVGlkeSBEYXRhDQoNCiJUaWR5IERhdGEiIGlzIGFuIGltcG9ydGFudCB3YXkgb2YgdGhpbmtpbmcgYWJvdXQgd2hhdCBkYXRhIHR5cGljYWxseSBsb29rIGxpa2UgaW4gUi4gTGV0J3MgZmV0Y2ggYSBmaWd1cmUgZnJvbSB0aGUgd2ViIHRvIHNob3cgdGhlIChwcmVmZXJyZWQpIHN0cnVjdHVyZSBvZiBkYXRhIGluIFIuIChUaGUgc3ludGF4IHRvIGJyaW5nIGluIGEgd2ViLWZpZ3VyZSBpcyBgIVtjYXB0aW9uXSh1cmwpYCkNCg0KIVtUaWR5IERhdGFdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucy9tYXN0ZXIvcnN0YXRzLWFydHdvcmsvdGlkeWRhdGFfMS5qcGcpDQpUaGUgdGhyZWUgZmVhdHVyZXMgZGVzY3JpYmVkIGluIHRoZSBmaWd1cmUgYWJvdmUgZGVmaW5lIHRoZSBuYXR1cmUgb2YgdGlkeSBkYXRhOg0KDQotICpWYXJpYWJsZXMgaW4gQ29sdW1ucyogIA0KLSAqT2JzZXJ2YXRpb25zIGluIFJvd3MqIGFuZCAgIA0KLSAqTWVhc3VyZW1lbnRzIGluIENlbGxzKi4NCg0KRGF0YSBhcmUgaW1hZ2luZWQgdG8gYmUgcmVzdWx0aW5nIGZyb20gYW4gKipleHBlcmltZW50KiouIEVhY2ggKnZhcmlhYmxlKiByZXByZXNlbnRzIGEgcGFyYW1ldGVyL2FzcGVjdCBpbiB0aGUgZXhwZXJpbWVudC4gRWFjaCAqcm93KiByZXByZXNlbnRzIGFuIGFkZGl0aW9uYWwgZGF0dW0gb2YgbWVhc3VyZW1lbnQuIEEgKmNlbGwqIGlzIGEgc2luZ2xlIG1lYXN1cmVtZW50IG9uIGEgc2luZ2xlIHBhcmFtZXRlcihjb2x1bW4pIGluIGEgc2luZ2xlIG9ic2VydmF0aW9uKHJvdykuDQoNCg0KIyMgS2luZHMgb2YgVmFyaWFibGVzDQoNCioqS2luZHMgb2YgVmFyaWFibGUqKiBhcmUgZGVmaW5lZCBieSB0aGUga2luZCBvZiAqcXVlc3Rpb25zKiB0aGV5IGFuc3dlciB0bzoNCg0KMS4gIFdoYXQvV2hvL1doZXJlPyAtPiBTb21lIGtpbmQgb2YgTmFtZS4gKipDYXRlZ29yaWNhbCoqIHZhcmlhYmxlDQoyLiAgV2hhdCBLaW5kPyBIb3c/IC0+IFNvbWUga2luZCBvZiAiVHlwZSIuICAqKkZhY3RvcioqIHZhcmlhYmxlDQozLiAgSG93IE1hbnk/IEhvdyBsYXJnZT8gLT4gU29tZSBraW5kIG9mIFF1YW50aXR5LiAqKk51bWVyaWNhbCoqIHZhcmlhYmxlLiANCk1vc3QgRmlndXJlcyBpbiBSIGFyZSBjb21wdXRlZCB3aXRoIHZhcmlhYmxlcywgYW5kIHRoZXJlZm9yZSwgd2l0aCAqKmNvbHVtbnMqKi4NCg0KIyBJbnRlcnJvZ2F0aW9ucyBhbmQgR3JhcGhzDQoNCkNyZWF0aW5nIGdyYXBocyBmcm9tIGRhdGEgaXMgYW4gYWN0IG9mIGFza2luZyBxdWVzdGlvbnMgYW5kIHZpZXdpbmcgYW5zd2VycyBpbiBhIGdlb21ldHJpYyB3YXkuIA0KTGV0IHVzIHdyaXRlIHNvbWUgc2ltcGxlIEVuZ2xpc2ggZGVzY3JpcHRpb25zIG9mIG1lYXN1cmVzIGFuZCB2aXN1YWxzIGFuZCBzZWUgd2hhdCBjb21tYW5kcyB0aGV5IHVzZSBpbiBSLg0KDQojIENvbXBvbmVudHMgb2YgdGhlIGxheWVyZWQgZ3JhbW1hciBvZiBncmFwaGljcw0KDQoqKkxheWVycyoqIGFyZSB1c2VkIHRvIGNyZWF0ZSB0aGUgb2JqZWN0cyBvbiBhIHBsb3QuIFRoZXkgYXJlIGRlZmluZWQgYnkgZml2ZSBiYXNpYyBwYXJ0czoNCg0KMS4gIERhdGEgKFdoYXQgZGF0YXNldC9zcHJlYWRzaGVldCBhbSBJIHVzaW5nPykNCjIuICBNYXBwaW5nIChXaGF0IGRvZXMgZWFjaCBjb2x1bW4gZG8gaW4gbXkgZ3JhcGg/KQ0KMy4gIFN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIChzdGF0KSAoRG8gSSBoYXZlIGNvdW50IHNvbWV0aGluZyBmaXJzdD8pDQo0LiAgR2VvbWV0cmljIG9iamVjdCAoZ2VvbSkgKFdoYXQgc2hhcGUsIGNvbG91ciwgc2l6ZS4uLmRvIEkgd2FudD8pDQo1LiAgUG9zaXRpb24gYWRqdXN0bWVudCAocG9zaXRpb24pIChXaGVyZSBkbyBJIHdhbnQgaXQgb24gdGhlIGdyYXBoPykNCg0KDQojIyBEYXRhey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KV2Ugd2lsbCB1c2UgInJlYWwgd29ybGQiIGRhdGEuIExldCdzIHVzZSB0aGUgYHBlbmd1aW5zYCBkYXRhc2V0IGluIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UuIFJ1biBgP3Blbmd1aW5zYCBpbiB0aGUgY29uc29sZSB0byBnZXQgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGlzIGRhdGFzZXQuDQoNCiMjIyBIZWFkDQpgYGB7ciBwZW5ndWlucy0xfQ0KaGVhZChwZW5ndWlucykNCmBgYA0KDQojIyMgVGFpbA0KYGBge3IgcGVuZ3VpbnMtMn0NCnRhaWwocGVuZ3VpbnMpDQpgYGANCg0KIyMjIERpbQ0KYGBge3IgcGVuZ3VpbnMtM30NCmRpbShwZW5ndWlucykNCmBgYA0KDQoNClNvIHdlIGtub3cgd2hhdCBvdXIgZGF0YSBsb29rcyBsaWtlLiBXZSBwYXNzIHRoaXMgZGF0YSB0byBgZ2dwbG90YCB1c2UgdG8gcGxvdCBhcyBmb2xsb3dzOiBpbiBSIHRoaXMgY3JlYXRlcyBhbiBlbXB0eSBncmFwaCBzaGVldCEhIEJlY2F1c2Ugd2UgaGF2ZSBub3QgKHlldCkgZGVjbGFyZWQgdGhlIGdlb21ldHJpYyBzaGFwZXMgd2Ugd2FudCB0byB1c2UgdG8gcGxvdCBvdXIgaW5mb3JtYXRpb24uIA0KDQoNCmBgYHtyIGRhdGEtMX0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICMgQ3JlYXRlcyBhbiBlbXB0eSBncmFwaHNoZWV0LCByZWFkeSBmb3IgcGxvdHRpbmchIQ0KDQpgYGANCg0KDQoNCg0KIyMgTWFwcGluZw0KDQpOb3cgdGhhdCB3ZSBoYXZlIHRvbGQgUiB3aGF0IGRhdGEgdG8gdXNlLCB3ZSBuZWVkIHRvIHN0YXRlIHdoYXQgdmFyaWFibGVzIHRvIHBsb3QgYW5kIGhvdy4gDQoNCioqQWVzdGhldGljIE1hcHBpbmcqKiBkZWZpbmVzIGhvdyB0aGUgdmFyaWFibGVzIGFyZSBhcHBsaWVkIHRvIHRoZSBwbG90LCBpLmUuIHdlIHRha2UgYSB2YXJpYWJsZSBmcm9tIHRoZSBkYXRhIGFuZCAibWV0YXBob3JpemUiIGl0IGludG8gYSBnZW9tZXRyaWMgZmVhdHVyZS4gV2UgY2FuIG1hcCB2YXJpYWJsZXMgbWV0YXBob3JpY2FsbHkgdG8gYSB2YXJpZXR5IG9mIGdlb21ldHJpYyB0aGluZ3M6IGNvb3JkaW5hdGUsIGxlbmd0aCwgaGVpZ2h0LCBzaXplLCBzaGFwZSwgY29sb3VyLCBhbHBoYShob3cgZGFyaz8pLi4uLg0KDQpUaGUgc3ludGF4IHVzZXM6IGBhZXMoc29tZV9nZW9tZXRyaWNfdGhpbmcgPSBzb21lX3ZhcmlhYmxlKWANCg0KUmVtZW1iZXIgKip2YXJpYWJsZSoqID0gKipjb2x1bW4qKi4gDQoNClNvIGlmIHdlIHdlcmUgZ3JhcGhpbmcgaW5mb3JtYXRpb24gZnJvbSBgcGVuZ3VpbnNgLCB3ZSBtaWdodCBtYXAgYSBwZW5ndWluJ3MgYGZsaXBwZXJfbGVuZ3RoX21tYCAqKmNvbHVtbioqIHRvIHRoZSAkeCQgcG9zaXRpb24sIGFuZCB0aGUgYGJvZHlfbWFzc19nYCAqKmNvbHVtbioqIHRvIHRoZSAkeSQgcG9zaXRpb24uDQoNCiMjIyBNYXBwaW5nIEV4YW1wbGUtMSB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpXZSBjYW4gdHJ5IGFub3RoZXIgZXhhbXBsZSBvZiBhZXN0aGV0aWMgbWFwcGluZyB3aXRoIHRoZSBzYW1lIGRhdGFzZXQ6DQoNCiMjIyMgUGxvdC0xYQ0KDQpgYGB7ciBtYXBwaW5nLTFhfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykNCmBgYA0KDQoNCg0KIyMjIyBQbG90LTFiDQoNCmBgYHtyIG1hcHBpbmctMWIsaW5jbHVkZT1GQUxTRX0NCmdncGxvdChwZW5ndWlucykgKyANCg0KIyBQbG90IGdlb20gPSBoaXN0b2dyYW0uIFNvIHdlIG5lZWQgYSBxdWFudGl0eSBvbiB0aGUgeA0KICBnZW9tX2hpc3RvZ3JhbSgNCiAgICBhZXMoeCA9IGJvZHlfbWFzc19nKSkNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQpkZWNvcmF0ZSgibWFwcGluZy0xYiIpICU+JSANCiAgZmxhaXJfbGluZXMoNDo1KSAlPiUgDQogIGtuaXRfcHJpbnQud2l0aF9mbGFpcigpDQpgYGANCg0KDQojIyMjIFBsb3QtMWMNCmBgYHtyIG1hcHBpbmctMWMsaW5jbHVkZSA9IEZBTFNFfQ0KZ2dwbG90KHBlbmd1aW5zKSArDQoNCiMgUGxvdCBnZW9tID0gaGlzdG9ncmFtLiBTbyB3ZSBuZWVkIGEgcXVhbnRpdHkgb24gdGhlIHgNCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgZmlsbCA9IGlzbGFuZCkgICAgIyBjb2xvciBhZXN0aGV0aWMgPSBhbm90aGVyIHZhcmlhYmxlDQogICAgKQ0KDQpgYGANCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmRlY29yYXRlKCJtYXBwaW5nLTFjIikgJT4lIA0KICBmbGFpcl9saW5lcyg1OjYpICU+JSANCiAga25pdF9wcmludC53aXRoX2ZsYWlyKCkNCg0KYGBgDQoNCiMjIyBNYXBwaW5nIEV4YW1wbGUtMiB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpXZSBjYW4gdHJ5IGFub3RoZXIgZXhhbXBsZSBvZiBhZXN0aGV0aWMgbWFwcGluZyB3aXRoIHRoZSBzYW1lIGRhdGFzZXQ6DQoNCiMjIyMgUGxvdC0yYQ0KDQpgYGB7ciBtYXBwaW5nLTJhfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykNCmBgYA0KDQoNCg0KIyMjIyBQbG90LTJiDQoNCmBgYHtyIG1hcHBpbmctMmIsaW5jbHVkZT1GQUxTRX0NCmdncGxvdChwZW5ndWlucykgKyANCg0KIyBQbG90IGdlb20gPSBoaXN0b2dyYW0uIFNvIHdlIG5lZWQgYSBxdWFudGl0eSBvbiB0aGUgeA0KICBnZW9tX2hpc3RvZ3JhbSgNCiAgICBhZXMoeCA9IGJvZHlfbWFzc19nKSkNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQpkZWNvcmF0ZSgibWFwcGluZy0yYiIpICU+JSANCiAgZmxhaXJfbGluZXMoNDo1KSAlPiUgDQogIGtuaXRfcHJpbnQud2l0aF9mbGFpcigpDQpgYGANCg0KDQojIyMjIFBsb3QtMmMNCmBgYHtyIG1hcHBpbmctMmMsaW5jbHVkZSA9IEZBTFNFfQ0KZ2dwbG90KHBlbmd1aW5zKSArDQoNCiMgUGxvdCBnZW9tID0gaGlzdG9ncmFtLiBTbyB3ZSBuZWVkIGEgcXVhbnRpdHkgb24gdGhlIHgNCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgZmlsbCA9IGlzbGFuZCkgICAgIyBjb2xvciBhZXN0aGV0aWMgPSBhbm90aGVyIHZhcmlhYmxlDQogICAgKQ0KDQpgYGANCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmRlY29yYXRlKCJtYXBwaW5nLTJjIikgJT4lIA0KICBmbGFpcl9saW5lcyg1OjYpICU+JSANCiAga25pdF9wcmludC53aXRoX2ZsYWlyKCkNCg0KYGBgDQoNCiMjIyBNYXBwaW5nIEV4YW1wbGUtMyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpXZSBjYW4gdHJ5IGFub3RoZXIgZXhhbXBsZSBvZiBhZXN0aGV0aWMgbWFwcGluZyB3aXRoIHRoZSBzYW1lIGRhdGFzZXQ6DQoNCiMjIyMgUGxvdC0zYQ0KDQpgYGB7ciBtYXBwaW5nLTNhfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykNCmBgYA0KDQoNCg0KIyMjIyBQbG90LTNiDQoNCmBgYHtyIG1hcHBpbmctM2IsaW5jbHVkZT1GQUxTRX0NCmdncGxvdChwZW5ndWlucykgKyANCg0KIyBQbG90IGdlb20gPSBoaXN0b2dyYW0uIFNvIHdlIG5lZWQgYSBxdWFudGl0eSBvbiB0aGUgeA0KICBnZW9tX2hpc3RvZ3JhbSgNCiAgICBhZXMoeCA9IGJvZHlfbWFzc19nKSkNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KDQpkZWNvcmF0ZSgibWFwcGluZy0zYiIpICU+JSANCiAgZmxhaXJfbGluZXMoNDo1KSAlPiUgDQogIGtuaXRfcHJpbnQud2l0aF9mbGFpcigpDQpgYGANCg0KDQojIyMjIFBsb3QtM2MNCmBgYHtyIG1hcHBpbmctM2MsaW5jbHVkZSA9IEZBTFNFfQ0KZ2dwbG90KHBlbmd1aW5zKSArDQoNCiMgUGxvdCBnZW9tID0gaGlzdG9ncmFtLiBTbyB3ZSBuZWVkIGEgcXVhbnRpdHkgb24gdGhlIHgNCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgZmlsbCA9IGlzbGFuZCkgICAgIyBjb2xvciBhZXN0aGV0aWMgPSBhbm90aGVyIHZhcmlhYmxlDQogICAgKQ0KDQpgYGANCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmRlY29yYXRlKCJtYXBwaW5nLTNjIikgJT4lIA0KICBmbGFpcl9saW5lcyg1OjYpICU+JSANCiAga25pdF9wcmludC53aXRoX2ZsYWlyKCkNCg0KYGBgDQoNCg0KDQojIyMgTWFwcGluZyBFeGFtcGxlLTQgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KV2UgY2FuIHRyeSBhbm90aGVyIGV4YW1wbGUgb2YgYWVzdGhldGljIG1hcHBpbmcgd2l0aCB0aGUgc2FtZSBkYXRhc2V0Og0KDQojIyMjIFBsb3QtNGENCg0KYGBge3IgbWFwcGluZy00YX0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpDQpgYGANCg0KDQoNCiMjIyMgUGxvdC00Yg0KDQpgYGB7ciBtYXBwaW5nLTRiLGluY2x1ZGU9RkFMU0V9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgDQoNCiMgUGxvdCBnZW9tID0gaGlzdG9ncmFtLiBTbyB3ZSBuZWVkIGEgcXVhbnRpdHkgb24gdGhlIHgNCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYWVzKHggPSBib2R5X21hc3NfZykpDQpgYGANCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KZGVjb3JhdGUoIm1hcHBpbmctNGIiKSAlPiUgDQogIGZsYWlyX2xpbmVzKDQ6NSkgJT4lIA0KICBrbml0X3ByaW50LndpdGhfZmxhaXIoKQ0KYGBgDQoNCg0KIyMjIyBQbG90LTRjDQpgYGB7ciBtYXBwaW5nLTRjLGluY2x1ZGUgPSBGQUxTRX0NCmdncGxvdChwZW5ndWlucykgKw0KDQojIFBsb3QgZ2VvbSA9IGhpc3RvZ3JhbS4gU28gd2UgbmVlZCBhIHF1YW50aXR5IG9uIHRoZSB4DQogIGdlb21faGlzdG9ncmFtKA0KICAgIGFlcyh4ID0gYm9keV9tYXNzX2csDQogICAgICAgIGZpbGwgPSBpc2xhbmQpICAgICMgY29sb3IgYWVzdGhldGljID0gYW5vdGhlciB2YXJpYWJsZQ0KICAgICkNCg0KYGBgDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQpkZWNvcmF0ZSgibWFwcGluZy00YyIpICU+JSANCiAgZmxhaXJfbGluZXMoNTo2KSAlPiUgDQogIGtuaXRfcHJpbnQud2l0aF9mbGFpcigpDQoNCmBgYA0KDQoNCiMjIEdlb21ldHJpYyBvYmplY3RzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCioqR2VvbWV0cmljIG9iamVjdHMqKiAoKmdlb21zKikgY29udHJvbCB0aGUgdHlwZSBvZiBwbG90IHlvdSBjcmVhdGUuIEdlb21zIGFyZSBjbGFzc2lmaWVkIGJ5IHRoZWlyIGRpbWVuc2lvbmFsaXR5Og0KDQotICAgMCBkaW1lbnNpb25zIC0gcG9pbnQsIHRleHQNCi0gICAxIGRpbWVuc2lvbiAtIHBhdGgsIGxpbmUNCi0gICAyIGRpbWVuc2lvbnMgLSBwb2x5Z29uLCBpbnRlcnZhbA0KDQpFYWNoIGdlb20gY2FuIG9ubHkgZGlzcGxheSBjZXJ0YWluICoqYWVzdGhldGljcyoqIG9yIHZpc3VhbCBhdHRyaWJ1dGVzIG9mIHRoZSBnZW9tLiBGb3IgZXhhbXBsZSwgYSBwb2ludCBnZW9tIGhhcyBwb3NpdGlvbiwgY29sb3IsIHNoYXBlLCBhbmQgc2l6ZSBhZXN0aGV0aWNzLg0KDQpXZSBjYW4gYWxzbyBzdGFjayB1cCBnZW9tcyBvbiB0b3Agb2Ygb25lIGFub3RoZXIgdG8gYWRkIGxheWVycyB0byB0aGUgZ3JhcGguDQoNCiMjIyBQbG90MQ0KYGBge3Igb3RoZXJfZ2VvbXMtMX0NCmdncGxvdChwZW5ndWlucywgYWVzKHggPSBmbGlwcGVyX2xlbmd0aF9tbSwgeSA9IGJvZHlfbWFzc19nKSkgKw0KICBnZW9tX2xpbmUoKQ0KYGBgDQoNCiMjIyBQbG90Mg0KYGBge3Igb3RoZXItZ2VvbXMtMn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsgDQogIGdlb21fbGluZShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSBib2R5X21hc3NfZykpDQpgYGANCg0KIyMjIFBsb3QzDQpgYGB7ciBvdGhlci1nZW9tcy0zfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykgKyANCiAgZ2VvbV9wb2ludChhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSBib2R5X21hc3NfZywNCiAgICAgICAgICAgICAgICAgY29sb3IgPSBpc2xhbmQsIA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IHNwZWNpZXMpKSArIA0KICANCiAgZ2d0aXRsZSgiQSBwb2ludCBnZW9tIHdpdGggcG9zaXRpb24gYW5kIGNvbG9yIGFuZCBzaGFwZSBhZXN0aGV0aWNzIikNCmBgYA0KDQoNCmBgYHtyIGdlb21fYmFyLTF9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICBhZXMoeCA9IHNwZWNpZXMpKSArICMgeCBwb3NpdGlvbiA9PiA/DQogICMgTm8gbmVlZCB0byB0eXBlICJtYXBwaW5nIi4uLg0KICBnZW9tX2JhcigpICsgIyBXaGVyZSBkb2VzIHRoZSBoZWlnaHQgY29tZSBmcm9tPw0KICBnZ3RpdGxlKCJBIGJhciBnZW9tIHdpdGggcG9zaXRpb24gYW5kIGhlaWdodCBhZXN0aGV0aWNzIikNCmBgYA0KDQpgYGB7ciBnZW9tX2Jhci0yfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgYWVzKHggPSBzcGVjaWVzKSkgKw0KICBnZW9tX2JhcigpICsNCiAgZ2d0aXRsZSgiQSBiYXIgZ2VvbSB3aXRoIHBvc2l0aW9uIGFuZCBoZWlnaHQgYWVzdGhldGljcyIpDQpgYGANCg0KLSAgIFBvc2l0aW9uIGRldGVybWluZXMgdGhlIHN0YXJ0aW5nIGxvY2F0aW9uIChvcmlnaW4pIG9mIGVhY2ggYmFyDQotICAgSGVpZ2h0IGRldGVybWluZXMgaG93IHRhbGwgdG8gZHJhdyB0aGUgYmFyLiBIZXJlIHRoZSBoZWlnaHQgaXMgYmFzZWQgb24gdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIGRhdGFzZXQgZm9yIGVhY2ggcG9zc2libGUgbnVtYmVyIG9mIGN5bGluZGVycy4NCg0KIyMgUG9zaXRpb24gYWRqdXN0bWVudCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpTb21ldGltZXMgd2l0aCBkZW5zZSBkYXRhIHdlIG5lZWQgdG8gYWRqdXN0IHRoZSBwb3NpdGlvbiBvZiBlbGVtZW50cyBvbiB0aGUgcGxvdCwgb3RoZXJ3aXNlIGRhdGEgcG9pbnRzIG1pZ2h0IG9ic2N1cmUgb25lIGFub3RoZXIuIEJhciBwbG90cyBmcmVxdWVudGx5ICoqc3RhY2sqKiBvciAqKmRvZGdlKiogdGhlIGJhcnMgdG8gYXZvaWQgb3ZlcmxhcDoNCg0KYGBge3IgZ2VvbV9iYXJfcG9zaXRpb25fc3RhY2tfYW5kX2RvZGdlfQ0KY291bnQoeCA9IHBlbmd1aW5zLCBzcGVjaWVzLCBpc2xhbmQpICU+JQ0KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gc3BlY2llcywgeSA9IG4sIGZpbGwgPSBpc2xhbmQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGdndGl0bGUobGFiZWwgPSAiQSBzdGFja2VkIGJhciBjaGFydCIpDQoNCmNvdW50KHggPSBwZW5ndWlucywgc3BlY2llcywgaXNsYW5kKSAlPiUNCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHNwZWNpZXMsIHkgPSBuLCBmaWxsID0gaXNsYW5kKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdndGl0bGUobGFiZWwgPSAiQSBkb2RnZWQgYmFyIGNoYXJ0IikNCmBgYA0KDQpgYGB7cn0NCnBlbmd1aW5zICU+JQ0KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gc3BlY2llcywgZmlsbCA9IGlzbGFuZCkpICsNCiAgZ2VvbV9iYXIoKSArDQogIGdndGl0bGUobGFiZWwgPSAiQSBzdGFja2VkIGJhciBjaGFydCIpDQoNCnBlbmd1aW5zICU+JQ0KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gc3BlY2llcywgZmlsbCA9IGlzbGFuZCkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGdndGl0bGUobGFiZWwgPSAiQSBkb2RnZWQgYmFyIGNoYXJ0IikNCmBgYA0KDQpTb21ldGltZXMgc2NhdHRlcnBsb3RzIHdpdGggZmV3IHVuaXF1ZSAkeCQgYW5kICR5JCB2YWx1ZXMgYXJlICoqaml0dGVyZWQqKiAocmFuZG9tIG5vaXNlIGlzIGFkZGVkKSB0byByZWR1Y2Ugb3ZlcnBsb3R0aW5nLg0KDQpgYGB7ciBwb3NpdGlvbn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICAgICAgeSA9IGJvZHlfbWFzc19nKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZ3RpdGxlKCJBIHBvaW50IGdlb20gd2l0aCBvYnNjdXJlZCBkYXRhIHBvaW50cyIpDQoNCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICAgICAgeSA9IGJvZHlfbWFzc19nKSkgKw0KICBnZW9tX2ppdHRlcigpICsNCiAgZ2d0aXRsZSgiQSBwb2ludCBnZW9tIHdpdGggaml0dGVyZWQgZGF0YSBwb2ludHMiKQ0KYGBgDQoNCiMjIFN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCkEgKipzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbioqICgqc3RhdCopIHByZS10cmFuc2Zvcm1zIHRoZSBkYXRhLCBiZWZvcmUgcGxvdHRpbmcuIEZvciBpbnN0YW5jZSwgaW4gYSBiYXIgZ3JhcGggeW91IG1pZ2h0IHN1bW1hcml6ZSB0aGUgZGF0YSBieSBgY291bnRgaW5nIHRoZSB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHdpdGhpbiBhIHNldCBvZiBjYXRlZ29yaWVzLCBhbmQgdGhlbiBwbG90dGluZyB0aGUgY291bnQuDQoNCiMjIyBDb3VudA0KYGBge3Igc3RhdC10cmFuc2Zvcm0tMX0NCmNvdW50KHggPSBwZW5ndWlucywgaXNsYW5kKQ0KYGBgDQojIyMgQ291bnQgYW5kIEJhciBHcmFwaA0KDQpgYGB7ciBzdGF0LXRyYW5zZm9ybS0yfQ0KbXlkYXQgPC0gY291bnQocGVuZ3VpbnMsaXNsYW5kKQ0KDQpnZ3Bsb3QoZGF0YSA9IG15ZGF0KSArDQogICAgZ2VvbV9jb2woYWVzKHggPSBpc2xhbmQsIHkgPSBuKSkNCmBgYA0KDQojIyMgVGlkeSBDb3VudCBhbmQgQmFyIEdyYXBoDQpgYGB7ciBzdGF0LXRyYW5zZm9ybS0zLGVjaG89RkFMU0V9DQpwZW5ndWlucyAlPiUgICAgICAgICAgICMgVGhpcyBJUyBhIHBpcGUgT3BlcmF0b3IhIQ0KICANCiAgY291bnQoLiwgaXNsYW5kKSAlPiUgIyAiLiIgcmVwcmVzZW50cyB3aGF0IGlzIHBhc3NlZCBmcm9tIHRoZSBwcmVjZWRpbmcgY29tbWFuZA0KICANCiAgZ2dwbG90KC4pICsgICAgDQogIA0KICBnZW9tX2NvbChhZXMoeCA9IGlzbGFuZCwgeSA9IG4pKQ0KDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmRlY29yYXRlKCJzdGF0LXRyYW5zZm9ybS0zIikgJT4lIA0KICBmbGFpcigiJT4lIikgJT4lIA0KICBmbGFpcigiLiIpICU+JSANCiAga25pdF9wcmludC53aXRoX2ZsYWlyKCkNCiAgDQpgYGANCg0KDQoNCiMjIyBDb3VudCBpbnNpZGUgdGhlIFBsb3QNCmBgYHtyIHN0YXQtdHJhbnNmb3JtLTR9DQpwZW5ndWlucyAlPiUgICAgICAgICAgICMgT3VyIHBpcGUgT3BlcmF0b3INCiAgDQogIGdncGxvdCguKSArICAgICAgICAgICMgIi4iIGJlY29tZXMgdGhlIHBlbmd1aW5zIGRhdGFzZXQNCiAgDQogIGdlb21fYmFyKGFlcyh4ID0gaXNsYW5kKSkgIyBOb3RlOiB5ID0gY291bnQsIGFuZCBpcyBjb21wdXRlZCBpbnRlcm5hbGx5ISENCg0KYGBgDQoNClNvbWV0aW1lcyB5b3UgZG9uJ3QgbmVlZCB0byBtYWtlIGEgc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb24uIEZvciBleGFtcGxlLCBpbiBhIHNjYXR0ZXJwbG90IHlvdSB1c2UgdGhlIHJhdyB2YWx1ZXMgZm9yIHRoZSAkeCQgYW5kICR5JCB2YXJpYWJsZXMgdG8gbWFwIG9udG8gdGhlIGdyYXBoLiBJbiB0aGVzZSBzaXR1YXRpb25zLCB0aGUgc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb24gaXMgYW4gKmlkZW50aXR5KiB0cmFuc2Zvcm1hdGlvbiAtIHRoZSBzdGF0IHNpbXBseSBwYXNzZXMgaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQgYW5kIGV4cG9ydHMgdGhlIGV4YWN0IHNhbWUgZGF0YXNldC4NCg0KIyMgU2NhbGUgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KQSAqKnNjYWxlKiogY29udHJvbHMgaG93IGRhdGEgaXMgbWFwcGVkIHRvIGFlc3RoZXRpYyBhdHRyaWJ1dGVzLCBzbyB3ZSBuZWVkIG9uZSBzY2FsZSBmb3IgZXZlcnkgYWVzdGhldGljIHByb3BlcnR5IGVtcGxveWVkIGluIGEgbGF5ZXIuIEZvciBleGFtcGxlLCB0aGlzIGdyYXBoIGRlZmluZXMgYSBzY2FsZSBmb3IgY29sb3I6DQoNCmBgYHtyIHNjYWxlLWNvbG9yfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gYmlsbF9kZXB0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICB5ID0gYmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBzcGVjaWVzKSkgKw0KICBnZW9tX3BvaW50KCkgDQoNCmBgYA0KIFRoZSBzY2FsZSBjYW4gYmUgY2hhbmdlZCB0byB1c2UgYSBkaWZmZXJlbnQgY29sb3IgcGFsZXR0ZToNCg0KYGBge3Igc2NhbGUtY29sb3ItcGFsZXR0ZX0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSBib2R5X21hc3NfZywgDQogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIixkaXJlY3Rpb24gPSAtMSkNCmBgYA0KDQpOb3cgd2UgYXJlIHVzaW5nIGEgZGlmZmVyZW50IHBhbGV0dGUsIGJ1dCB0aGUgc2NhbGUgaXMgc3RpbGwgY29uc2lzdGVudDogYWxsIEFkZWxpZSBwZW5ndWlucyB1dGlsaXplIHRoZSBzYW1lIGNvbG9yLCB3aGVyZWFzIENoaW5zdHJhcCB1c2UgYSBuZXcgY29sb3IgKipidXQgZWFjaCBBZGVsaWUgc3RpbGwgdXNlcyB0aGUgc2FtZSwgY29uc2lzdGVudCBjb2xvcioqLg0KDQojIyBDb29yZGluYXRlIHN5c3RlbSB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpBICoqY29vcmRpbmF0ZSBzeXN0ZW0qKiAoKmNvb3JkKikgbWFwcyB0aGUgcG9zaXRpb24gb2Ygb2JqZWN0cyBvbnRvIHRoZSBwbGFuZSBvZiB0aGUgcGxvdCwgYW5kIGNvbnRyb2xzIGhvdyB0aGUgYXhlcyBhbmQgZ3JpZCBsaW5lcyBhcmUgZHJhd24uIFBsb3RzIHR5cGljYWxseSB1c2UgdHdvIGNvb3JkaW5hdGVzICgkeCwgeSQpLCBidXQgY291bGQgdXNlIGFueSBudW1iZXIgb2YgY29vcmRpbmF0ZXMuIE1vc3QgcGxvdHMgYXJlIGRyYXduIHVzaW5nIHRoZSBbKipDYXJ0ZXNpYW4gY29vcmRpbmF0ZSBzeXN0ZW0qKl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2FydGVzaWFuX2Nvb3JkaW5hdGVfc3lzdGVtKToNCg0KYGBge3IgY29vcmQtY2FydH0NCngxIDwtIGMoMSwgMTApDQp5MSA8LSBjKDEsIDUpDQpwIDwtIHFwbG90KHggPSB4MSwgeSA9IHkxLCBnZW9tID0gInBvaW50IiwgeGxhYiA9IE5VTEwsIHlsYWIgPSBOVUxMKSArDQogIHRoZW1lX2J3KCkNCnAgKw0KICBnZ3RpdGxlKGxhYmVsID0gIkNhcnRlc2lhbiBjb29yZGluYXRlIHN5c3RlbSIpDQoNCg0KZ2dwbG90KHBlbmd1aW5zLCBhZXMoZmxpcHBlcl9sZW5ndGhfbW0sIGJvZHlfbWFzc19nKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgY29vcmRfcG9sYXIoKQ0KYGBgDQoNClRoaXMgc3lzdGVtIHJlcXVpcmVzIGEgZml4ZWQgYW5kIGVxdWFsIHNwYWNpbmcgYmV0d2VlbiB2YWx1ZXMgb24gdGhlIGF4ZXMuIA0KVGhhdCBpcywgdGhlIGdyYXBoIGRyYXdzIHRoZSBzYW1lIGRpc3RhbmNlIGJldHdlZW4gMSBhbmQgMiBhcyBpdCBkb2VzIGJldHdlZW4gNSBhbmQgNi4gVGhlIGdyYXBoIGNvdWxkIGJlIGRyYXduIHVzaW5nIGEgWyoqc2VtaS1sb2cgY29vcmRpbmF0ZSBzeXN0ZW0qKl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU2VtaS1sb2dfcGxvdCkgd2hpY2ggbG9nYXJpdGhtaWNhbGx5IGNvbXByZXNzZXMgdGhlIGRpc3RhbmNlIG9uIGFuIGF4aXM6DQoNCmBgYHtyIGNvb3JkX3NlbWlfbG9nfQ0KcCArDQogIGNvb3JkX3RyYW5zKHkgPSAibG9nMTAiKSArDQogIGdndGl0bGUobGFiZWwgPSAiU2VtaS1sb2cgY29vcmRpbmF0ZSBzeXN0ZW0iKQ0KYGBgDQoNCg0KDQpPciBjb3VsZCBldmVuIGJlIGRyYXduIHVzaW5nIFsqKnBvbGFyIGNvb3JkaW5hdGVzKipdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1BvbGFyX2Nvb3JkaW5hdGVfc3lzdGVtKToNCg0KYGBge3IgY29vcmRfcG9sYXJ9DQpwICsNCiAgY29vcmRfcG9sYXIoKSArDQogIGdndGl0bGUobGFiZWwgPSAiUG9sYXIgY29vcmRpbmF0ZSBzeXN0ZW0iKQ0KYGBgDQoNCiMjIEZhY2V0aW5nIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCg0KKipGYWNldGluZyoqIGNhbiBiZSB1c2VkIHRvIHNwbGl0IHRoZSBkYXRhIHVwIGludG8gc3Vic2V0cyBvZiB0aGUgZW50aXJlIGRhdGFzZXQuIFRoaXMgaXMgYSBwb3dlcmZ1bCB0b29sIHdoZW4gaW52ZXN0aWdhdGluZyB3aGV0aGVyIHBhdHRlcm5zIGFyZSB0aGUgc2FtZSBvciBkaWZmZXJlbnQgYWNyb3NzIGNvbmRpdGlvbnMsIGFuZCBhbGxvd3MgdGhlIHN1YnNldHMgdG8gYmUgdmlzdWFsaXplZCBvbiB0aGUgc2FtZSBwbG90IChrbm93biBhcyAqKmNvbmRpdGlvbmVkKiogb3IgKip0cmVsbGlzKiogcGxvdHMpLiBUaGUgZmFjZXRpbmcgc3BlY2lmaWNhdGlvbiBkZXNjcmliZXMgd2hpY2ggdmFyaWFibGVzIHNob3VsZCBiZSB1c2VkIHRvIHNwbGl0IHVwIHRoZSBkYXRhLCBhbmQgaG93IHRoZXkgc2hvdWxkIGJlIGFycmFuZ2VkLg0KDQpgYGB7ciBmYWNldC0xfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgeSA9IGJvZHlfbWFzc19nKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBmYWNldF93cmFwKH4gaXNsYW5kKQ0KYGBgDQoNCg0KYGBge3IgZmFjZXQtMn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYm9keV9tYXNzX2csIGNvbG9yID0gc2V4KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBmYWNldF9ncmlkKHNwZWNpZXMgfiBpc2xhbmQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KDQojIFJpYSdzIGV4cGxhbmF0aW9uOiBUaGlzIGNvZGUgZGlkIG5vdCB3b3JrIGJlY2FzdWUuLi4uDQpgYGANCg0KDQojIyBEZWZhdWx0cw0KDQpSYXRoZXIgdGhhbiBleHBsaWNpdGx5IGRlY2xhcmluZyBlYWNoIGNvbXBvbmVudCBvZiBhIGxheWVyZWQgZ3JhcGhpYyAod2hpY2ggd2lsbCB1c2UgbW9yZSBjb2RlIGFuZCBpbnRyb2R1Y2VzIG9wcG9ydHVuaXRpZXMgZm9yIGVycm9ycyksIHdlIGNhbiBlc3RhYmxpc2ggaW50ZWxsaWdlbnQgZGVmYXVsdHMgZm9yIHNwZWNpZmljIGdlb21zIGFuZCBzY2FsZXMuIEZvciBpbnN0YW5jZSwgd2hlbmV2ZXIgd2Ugd2FudCB0byB1c2UgYSBiYXIgZ2VvbSwgd2UgY2FuIGRlZmF1bHQgdG8gdXNpbmcgYSBzdGF0IHRoYXQgY291bnRzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggZ3JvdXAgb2Ygb3VyIHZhcmlhYmxlIGluIHRoZSAkeCQgcG9zaXRpb24uDQoNCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgc2NlbmFyaW86IHlvdSB3aXNoIHRvIGdlbmVyYXRlIGEgc2NhdHRlcnBsb3QgdmlzdWFsaXppbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHBlbmd1aW5zJyBiaWxsX2xlbmd0aCBhbmQgdGhlaXIgYm9keV9tYXNzLiBXaXRoIG5vIGRlZmF1bHRzLCB0aGUgY29kZSB0byBnZW5lcmF0ZSB0aGlzIGdyYXBoIGlzOg0KDQpgYGB7ciBkZWZhdWx0fQ0KZ2dwbG90KCkgKw0KICBsYXllcigNCiAgICBkYXRhID0gcGVuZ3VpbnMsIA0KICAgIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgIHkgPSBib2R5X21hc3NfZyksDQogICAgZ2VvbSA9ICJwb2ludCIsIA0KICAgIHN0YXQgPSAiaWRlbnRpdHkiLCANCiAgICBwb3NpdGlvbiA9ICJpZGVudGl0eSINCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cygpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKCkgKw0KICBjb29yZF9jYXJ0ZXNpYW4oKQ0KYGBgDQoNClRoZSBhYm92ZSBjb2RlOg0KDQotICAgQ3JlYXRlcyBhIG5ldyBwbG90IG9iamVjdCAoYGdncGxvdGApDQoNCi0gICBBZGRzIGEgbGF5ZXIgKGBsYXllcmApDQoNCiAgICAtICAgU3BlY2lmaWVzIHRoZSBkYXRhIChgcGVuZ3VpbnNgKQ0KICAgIC0gICBNYXBzIGVuZ2luZSBiaWxsIGxlbmd0aCB0byB0aGUgJHgkIHBvc2l0aW9uIGFuZCBib2R5IG1hc3MgdG8gdGhlICR5JCBwb3NpdGlvbiAoYG1hcHBpbmdgKQ0KICAgIC0gICBVc2VzIHRoZSBwb2ludCBnZW9tZXRyaWMgdHJhbnNmb3JtYXRpb24gKGBnZW9tID0gInBvaW50ImApDQogICAgLSAgIEltcGxlbWVudHMgYW4gaWRlbnRpdHkgdHJhbnNmb3JtYXRpb24gYW5kIHBvc2l0aW9uIChgc3RhdCA9ICJpZGVudGl0eSJgIGFuZCBgcG9zaXRpb24gPSAiaWRlbnRpdHkiYCkNCg0KLSAgIEVzdGFibGlzaGVzIHR3byBjb250aW51b3VzIHBvc2l0aW9uIHNjYWxlcyAoYHNjYWxlX3hfY29udGludW91c2AgYW5kIGBzY2FsZV95X2NvbnRpbnVvdXNgKQ0KDQotICAgRGVjbGFyZXMgYSBjYXJ0ZXNpYW4gY29vcmRpbmF0ZSBzeXN0ZW0gKGBjb29yZF9jYXJ0ZXNpYW5gKQ0KDQpIb3cgY2FuIHdlIHNpbXBsaWZ5IHRoaXMgdXNpbmcgaW50ZWxsaWdlbnQgZGVmYXVsdHM/DQoNCjEuICBXZSBvbmx5IG5lZWQgdG8gc3BlY2lmeSBvbmUgZ2VvbSBhbmQgc3RhdCwgc2luY2UgZWFjaCBnZW9tIGhhcyBhIGRlZmF1bHQgc3RhdC4NCg0KMi4gIENhcnRlc2lhbiBjb29yZGluYXRlIHN5c3RlbXMgYXJlIG1vc3QgY29tbW9ubHkgdXNlZCwgc28gaXQgc2hvdWxkIGJlIHRoZSBkZWZhdWx0Lg0KDQozLiAgRGVmYXVsdCBzY2FsZXMgY2FuIGJlIGFkZGVkIGJhc2VkIG9uIHRoZSBhZXN0aGV0aWMgYW5kIHR5cGUgb2YgdmFyaWFibGVzLg0KDQogICAgLSAgIENvbnRpbnVvdXMgdmFsdWVzIGFyZSB0cmFuc2Zvcm1lZCB3aXRoIGEgbGluZWFyIHNjYWxpbmcuDQogICAgLSAgIERpc2NyZXRlIHZhbHVlcyBhcmUgbWFwcGVkIHRvIGludGVnZXJzLg0KICAgIC0gICBTY2FsZXMgZm9yIGFlc3RoZXRpY3Mgc3VjaCBhcyBjb2xvciwgZmlsbCwgYW5kIHNpemUgY2FuIGFsc28gYmUgaW50ZWxsaWdlbnRseSBkZWZhdWx0ZWQuDQoNClVzaW5nIHRoZXNlIGRlZmF1bHRzLCB3ZSBjYW4gcmV3cml0ZSB0aGUgYWJvdmUgY29kZSBhczoNCg0KYGBge3IgZGVmYXVsdDJ9DQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYm9keV9tYXNzX2cpKQ0KYGBgDQoNClRoaXMgZ2VuZXJhdGVzIHRoZSBleGFjdCBzYW1lIHBsb3QsIGJ1dCB1c2VzIGZld2VyIGxpbmVzIG9mIGNvZGUuIEJlY2F1c2UgbXVsdGlwbGUgbGF5ZXJzIGNhbiB1c2UgdGhlIHNhbWUgY29tcG9uZW50cyAoZGF0YSwgbWFwcGluZywgZXRjLiksIHdlIGNhbiBhbHNvIHNwZWNpZnkgdGhhdCBpbmZvcm1hdGlvbiBpbiB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbiByYXRoZXIgdGhhbiBpbiB0aGUgYGxheWVyKClgIGZ1bmN0aW9uOg0KDQpgYGB7ciBkZWZhdWx0M30NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSBib2R5X21hc3NfZykpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KQW5kIGFzIHdlIHdpbGwgbGVhcm4sIGZ1bmN0aW9uIGFyZ3VtZW50cyBpbiBSIHVzZSBzcGVjaWZpYyBvcmRlcmluZywgc28gd2UgY2FuIG9taXQgdGhlIGV4cGxpY2l0IGNhbGwgdG8gYGRhdGFgIGFuZCBgbWFwcGluZ2A6DQoNCmBgYHtyIGRlZmF1bHQ0fQ0KZ2dwbG90KHBlbmd1aW5zLCBhZXMoYmlsbF9sZW5ndGhfbW0sIGJvZHlfbWFzc19nKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQoNCmBgYHtyIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9pc2FiZWxsYS1iLmNvbS9ibG9nL2dncGxvdDItdGhlbWUtZWxlbWVudHMtcmVmZXJlbmNlL2dncGxvdDItdGhlbWUtZWxlbWVudHMtcmVmZXJlbmNlLXYyX2h1ODk5NDA5MGUxOTYwYTBhNzE4NzhhMzc1NmRhMjAwNzZfNTgwODE5XzIwMDB4MjAwMF9maXRfbGFuY3pvc18yLnBuZyIpDQoNCmBgYA0KDQo=