Skip to content Skip to sidebar Skip to footer

Why is the Legend Changing From Continuous to Categorical Ggplot

Introduction

The mtcars dataset contains information on 32 cars from a 1973 issue of Motor Trend magazine. This dataset is small, intuitive, and contains a variety of continuous and categorical variables.

            # Load the ggplot2 package library(ggplot2)          
            package 㤼㸱ggplot2㤼㸲 was built under R version 3.6.3          
            library(dplyr)          
                          Attaching package: 㤼㸱dplyr㤼㸲  The following objects are masked from 㤼㸱package:stats㤼㸲:      filter, lag  The following objects are masked from 㤼㸱package:base㤼㸲:      intersect, setdiff, setequal, union          
            # Explore the mtcars data frame with str() str(mtcars)          
            'data.frame':   32 obs. of  13 variables:  $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...  $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...  $ disp: num  160 160 108 258 360 ...  $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...  $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...  $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...  $ qsec: num  16.5 17 18.6 19.4 17 ...  $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...  $ am  : num  1 1 1 0 0 0 0 0 0 0 ...  $ gear: num  4 4 4 3 3 3 3 4 4 4 ...  $ carb: num  4 4 1 1 2 1 4 2 2 4 ...  $ fcyl: Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ...  $ fam : Factor w/ 2 levels "automatic","manual": 2 2 2 1 1 1 1 1 1 1 ...          
            # Execute the following command ggplot(mtcars, aes(cyl, mpg)) +   geom_point()          

Notice that ggplot2 treats cyl as a continuous variable. We get a plot, but it's not quite right, because it gives the impression that there is such a thing as a 5 or 7-cylinder car, which there is not.

Data columns types affect plot types

Although cyl (the number of cylinders) is categorical, you probably noticed that it is classified as numeric in mtcars. This is really misleading because the representation in the plot doesn't match the actual data type. You'll have to explicitly tell ggplot2 that cyl is a categorical variable.

              # Load the ggplot2 package library(ggplot2)  # Change the command below so that cyl is treated as factor ggplot(mtcars, aes(factor(cyl), mpg)) +   geom_point()            

Notice that ggplot2 treats cyl as a factor. This time the x-axis does not contain variables like 5 or 7, only the values that are present in the dataset.

The grammar of graphics

Mapping data columns to aesthetics

Let's dive a little deeper into the three main topics in this course: The data, aesthetics, and geom layers.

                # Edit to add a color aesthetic mapped to disp ggplot(mtcars, aes(wt, mpg, color = disp)) +   geom_point()              

                # Change the color aesthetic to a size aesthetic ggplot(mtcars, aes(wt, mpg, size = disp)) +   geom_point()              

Understanding variables

In the previous exercise you saw that disp can be mapped onto a color gradient or onto a continuous size scale.

Another argument of aes() is the shape of the points. There are a finite number of shapes which ggplot() can automatically assign to the points.

ggplot2 layers

Adding geometries

The diamonds dataset contains details of 1,000 diamonds. Among the variables included are carat (a measurement of the diamond's size) and price.

We'll use two common geom layer functions: - geom_point() adds points (as in a scatter plot). - geom_smooth() adds a smooth trend curve.

                # Explore the diamonds data frame with str() str(diamonds)              
                Classes 'tbl_df', 'tbl' and 'data.frame':   53940 obs. of  10 variables:  $ carat  : num  0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...  $ cut    : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...  $ color  : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...  $ clarity: Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...  $ depth  : num  61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...  $ table  : num  55 61 65 58 58 57 57 55 61 61 ...  $ price  : int  326 326 327 334 335 336 336 337 337 338 ...  $ x      : num  3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...  $ y      : num  3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...  $ z      : num  2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...              
                # Add geom_point() with + ggplot(diamonds, aes(carat, price)) +   geom_point()              

                # Add geom_smooth() with + ggplot(diamonds, aes(carat, price)) +   geom_point() +   geom_smooth()              

Changing one geom or every geom

If we have multiple geoms, then mapping an aesthetic to data variable inside the call to ggplot() will change all the geoms. It is also possible to make changes to individual geoms by passing arguments to the geom_*() functions.

geom_point() has an alpha argument that controls the opacity of the points. A value of 1 (the default) means that the points are totally opaque; a value of 0 means the points are totally transparent (and therefore invisible). Values in between specify transparency.

                # Map the color aesthetic to clarity ggplot(diamonds, aes(carat, price, color = clarity)) +   geom_point() +   geom_smooth()              

                # Make the points 40% opaque ggplot(diamonds, aes(carat, price, color = clarity)) +   geom_point(alpha = 0.4) +   geom_smooth()              

Saving plots as variables

Plots can be saved as variables, which can be added two later on using the + operator. This is really useful if you want to make multiple related plots from a common base.

                # Draw a ggplot plt_price_vs_carat <- ggplot(   # Use the diamonds dataset   diamonds,   # For the aesthetics, map x to carat and y to price   aes(carat, price) )  # Add a point layer to plt_price_vs_carat plt_price_vs_carat + geom_point()              

                # Edit this to make points 20% opaque: plt_price_vs_carat_transparent plt_price_vs_carat_transparent <- plt_price_vs_carat + geom_point(alpha = 0.2)  # See the plot plt_price_vs_carat_transparent              

                # Edit this to map color to clarity, # Assign the updated plot to a new object plt_price_vs_carat_by_clarity <- plt_price_vs_carat + geom_point(aes(color = clarity))  # See the plot plt_price_vs_carat_by_clarity              

By assigning parts of plots to a variable then reusing that variable in other plots, it makes it really clear how much those plots have in common.

Aesthetics

Color, shape and size

These are the aesthetics we can consider within aes() in this chapter: x, y, color, fill, size, alpha, labels and shape.

One common convention is that you don't name the x and y arguments to aes(), since they almost always come first, but you do name other arguments.

              #create factor fcyl mtcars <- mtcars %>%    mutate(fcyl = as.factor(cyl),          fam = as.factor(am))            
              library(forcats) mtcars <- mtcars %>%    mutate(fam = fct_recode(fam,                           "manual" = "1",                          "automatic" = "0"))            
              # Map x to mpg and y to fcyl ggplot(mtcars, aes(mpg, fcyl)) +   geom_point()                          

              # Swap mpg and fcyl ggplot(mtcars, aes(fcyl, mpg)) +   geom_point()            

              # Map x to wt, y to mpg and color to fcyl ggplot(mtcars, aes(wt, mpg, color = fcyl)) +   geom_point()            

              ggplot(mtcars, aes(wt, mpg, color = fcyl)) +   # Set the shape and size of the points   geom_point(shape = 1, size = 4)            

Color vs. fill

Typically, the color aesthetic changes the outline of a geom and the fill aesthetic changes the inside. geom_point() is an exception: you use color (not fill) for the point color. However, some shapes have special behavior.

The default geom_point() uses shape = 19: a solid circle. An alternative is shape = 21: a circle that allow you to use both fill for the inside and color for the outline. This is lets you to map two aesthetics to each point.

All shape values are described on the points() help page.

              # Map fcyl to fill ggplot(mtcars, aes(wt, mpg, fill = fcyl)) +   geom_point(shape = 1, size = 4)            

              ggplot(mtcars, aes(wt, mpg, fill = fcyl)) +   # Change point shape; set alpha   geom_point(shape = 21, size = 4, alpha = 0.6)            

              # Map color to fam ggplot(mtcars, aes(wt, mpg, fill = fcyl, color = fam)) +   geom_point(shape = 21, size = 4, alpha = 0.6)            

Notice that mapping a categorical variable onto fill doesn't change the colors, although a legend is generated! This is because the default shape for points only has a color attribute and not a fill attribute! Use fill when you have another shape (such as a bar), or when using a point that does have a fill and a color attribute, such as shape = 21, which is a circle with an outline. Any time you use a solid color, make sure to use alpha blending to account for over plotting.

Comparing aesthetics

Be careful of a major pitfall: these attributes can overwrite the aesthetics of your plot!

              # Establish the base layer plt_mpg_vs_wt <- ggplot(mtcars, aes(wt, mpg))            
              # Map fcyl to size plt_mpg_vs_wt +   geom_point(aes(size = fcyl))            

              plt_mpg_vs_wt +   geom_point(aes(alpha = fcyl))            

              # Map fcyl to shape, not alpha plt_mpg_vs_wt +   geom_point(aes(shape = fcyl))            

              # Use text layer and map fcyl to label plt_mpg_vs_wt +   geom_text(aes(label = fcyl))            

Label and shape are only applicable to categorical data.

Color, shape, size and alpha

This time we'll use these arguments to set attributes of the plot, not map variables onto aesthetics.

We can specify colors in R using hex codes: a hash followed by two hexadecimal numbers each for red, green, and blue ("#RRGGBB"). Hexadecimal is base-16 counting. We have 0 to 9, and A representing 10 up to F representing 15. Pairs of hexadecimal numbers give you a range from 0 to 255. "#000000" is "black" (no color), "#FFFFFF" means "white", and `"#00FFFF" is cyan (mixed green and blue).

              # A hexadecimal color my_blue <- "#4ABEFF"            
              ggplot(mtcars, aes(wt, mpg)) +   # Set the point color and alpha   geom_point(color = my_blue, alpha = 0.6)            

              # Change the color mapping to a fill mapping ggplot(mtcars, aes(wt, mpg, fill = fcyl)) +   # Set point size and shape   geom_point(color = my_blue, size = 10, shape = 1)            

ggplot2 lets you control these attributes in many ways to customize your plots.

Conflicts with aesthetics

We can use all the aesthetics as attributes. Let's see how this works with the aesthetics you used in the previous exercises: x, y, color, fill, size, alpha, label and shape.

              ggplot(mtcars, aes(wt, mpg, color = fcyl)) +   # Add point layer with alpha 0.5   geom_point(alpha = 0.5)            

              ggplot(mtcars, aes(wt, mpg, color = fcyl)) +   # Add text layer with label rownames(mtcars) and color red   geom_text(label = rownames(mtcars), color = "red")            

              ggplot(mtcars, aes(wt, mpg, color = fcyl)) +   # Add points layer with shape 24 and color yellow   geom_point(shape = 24, color = "yellow")            

Going all out

Now, we will gradually add more aesthetics layers to the plot. We're still working with the mtcars dataset, but this time we're using more features of the cars. Each of the columns is described on the mtcars help page.

Notice that adding more aesthetic mappings to our plot is not always a good idea! We may just increase complexity and decrease readability.

              # 3 aesthetics: qsec vs. mpg, colored by fcyl ggplot(mtcars, aes(mpg, qsec, color = fcyl)) +   geom_point()            

              # 4 aesthetics: add a mapping of shape to fam ggplot(mtcars, aes(mpg, qsec, color = fcyl, shape = fam)) +   geom_point()            

              # 5 aesthetics: add a mapping of size to hp / wt ggplot(mtcars, aes(mpg, qsec, color = fcyl, shape = fam, size = hp/wt)) +   geom_point()            

Between the x and y dimensions, the color, shape, and size of the points, your plot displays five dimensions of the dataset!

Modifying aesthetics

Updating aesthetic labels

We'll modify some aesthetics to make a bar plot of the number of cylinders for cars with different types of transmission.

We'll also make use of some functions for improving the appearance of the plot.

  • labs() to set the x- and y-axis labels. It takes strings for each argument.
  • cale_color_manual() defines properties of the color scale (i.e. axis). The first argument sets the legend title. values is a named vector of colors to use.
              ggplot(mtcars, aes(fcyl, fill = fam)) +   geom_bar() +   # Set the axis labels   labs(x = "Number of Cylinders", y = "Count")            

              palette <- c(automatic = "#377EB8", manual = "#E41A1C")            
              ggplot(mtcars, aes(fcyl, fill = fam)) +   geom_bar() +   labs(x = "Number of Cylinders", y = "Count") +   # Set the fill color scale   scale_fill_manual("Transmission", values = palette)            

              # Set the position ggplot(mtcars, aes(fcyl, fill = fam)) +   geom_bar(position = "dodge") +   labs(x = "Number of Cylinders", y = "Count") +   scale_fill_manual("Transmission", values = palette)            

Choosing the right position argument is an important part of making a good plot.

Setting a dummy aesthetic

We saw all the visible aesthetics can serve as attributes and aesthetics, but we left out x and y. That's because although we can make univariate plots (such as histograms, which you'll get to in the next chapter), a y-axis will always be provided, even if you didn't ask for it.

We can make univariate plots in ggplot2, but we will need to add a fake y axis by mapping y to zero.

When using setting y-axis limits, we can specify the limits as separate arguments, or as a single numeric vector. That is, ylim(lo, hi) or ylim(c(lo, hi)).

              # Plot 0 vs. mpg ggplot(mtcars, aes(mpg, 0)) +   # Add jitter    geom_point(position = "jitter")                          

              ggplot(mtcars, aes(mpg, 0)) +   geom_jitter() +   # Set the y-axis limits   ylim(-2, 2)            

The best way to make your plot depends on a lot of different factors and sometimes ggplot2 might not be the best choice.

Aesthetics best practices

Appropriate mappings

Incorrect aesthetic mapping causes confusion or misleads the audience.

Typically, the dependent variable is mapped onto the the y-axis and the independent variable is mapped onto the x-axis. aesthetics

Form follows function

Function

Primary:

  • Accurate and efficient representations

Secondary:

  • Visually appealing, beautiful plots

Guiding principles

Never:

  • Misrepresent or obscure data
  • Confuse viewers with complexity

Always:

  • Consider the audience and purpose of every plot

The best choices for aesthetics

  • Effficient

    • Provides a faster overview than numeric summaries
  • Accurate

    • Minimizes information loss

Geometries

Scatter Plots: Overplotting

Large datasets

Scatter plots (using geom_point()) are intuitive, easily understood, and very common, but we must always consider overplotting, particularly in the following four situations:

  1. Large datasets
  2. Aligned values on a single axis
  3. Low-precision data
  4. Integer data

Typically, alpha blending (i.e. adding transparency) is recommended when using solid shapes. Alternatively, you can use opaque, hollow shapes.

Small points are suitable for large datasets with regions of high density (lots of overlapping).

                # Plot price vs. carat, colored by clarity plt_price_vs_carat_by_clarity <- ggplot(diamonds, aes(carat, price, color = clarity)) # Add a point layer with tiny points plt_price_vs_carat_by_clarity + geom_point(alpha = 0.5, shape = ".")              

                # Set transparency to 0.5, set shape to 16 plt_price_vs_carat_by_clarity + geom_point(alpha = 0.5, shape = 16)              

Aligned values

Let's take a look at another case where we should be aware of overplotting: Aligning values on a single axis.

This occurs when one axis is continuous and the other is categorical, which can be overcome with some form of jittering.

                # Plot base plt_mpg_vs_fcyl_by_fam <- ggplot(mtcars, aes(fcyl, mpg, color = fam))              
                # Default points are shown for comparison plt_mpg_vs_fcyl_by_fam + geom_point()              

                # Default points are shown for comparison plt_mpg_vs_fcyl_by_fam + geom_point()              

                # Alter the point positions by jittering, width 0.3 plt_mpg_vs_fcyl_by_fam + geom_point(position = position_jitter(width = 0.3))              

                # Default points are shown for comparison plt_mpg_vs_fcyl_by_fam + geom_point()              

                # Now jitter and dodge the point positions plt_mpg_vs_fcyl_by_fam + geom_point(position = position_jitterdodge(jitter.width = 0.3, dodge.width = 0.3))              

Low-precision data

Overplotting 3: Low-precision data We already saw how to deal with overplotting when using geom_point() in two cases:

  1. Large datasets
  2. Aligned values on a single axis

We used position = 'jitter' inside geom_point() or geom_jitter().

Let's take a look at another case:

  1. Low-precision data

This results from low-resolution measurements like in the iris dataset, which is measured to 1mm precision. It's similar to case 2, but in this case we can jitter on both the x and y axis.

                ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +   # Swap for jitter layer with width 0.1   geom_jitter(alpha = 0.5,width = 0.1)              

                #jitter within geom_point ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +   # Set the position to jitter   geom_point(alpha = 0.5, position = "jitter")              

                ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +   # Use a jitter position function with width 0.1   geom_point(alpha = 0.5, position = position_jitter(width = 0.1))              

Notice that jitter can be a geom itself (i.e. geom_jitter()), an argument in geom_point() (i.e. position = "jitter"), or a position function, (i.e. position_jitter()).

Integer data

Let's take a look at the last case of dealing with overplotting:

  1. Integer data

This can be type integer (i.e. 1 ,2, 3…) or categorical (i.e. class factor) variables. factor is just a special class of type integer.

We'll typically have a small, defined number of intersections between two variables, which is similar to case 3, but you may miss it if you don't realize that integer and factor data are the same as low precision data.

The Vocab dataset provided contains the years of education and vocabulary test scores from respondents to US General Social Surveys from 1972-2004.

                library(carData)              
                                  Attaching package: 㤼㸱carData㤼㸲  The following object is masked _by_ 㤼㸱.GlobalEnv㤼㸲:      Vocab              
                # Examine the structure of Vocab str(Vocab)              
                'data.frame':   30351 obs. of  4 variables:  $ year      : num  1974 1974 1974 1974 1974 ...  $ sex       : Factor w/ 2 levels "Female","Male": 2 2 1 1 1 2 2 2 1 1 ...  $ education : num  14 16 10 10 12 16 17 10 12 11 ...  $ vocabulary: Factor w/ 11 levels "0","1","2","3",..: 10 10 10 6 9 9 10 6 4 6 ...              
                # Plot vocabulary vs. education ggplot(Vocab, aes(education, vocabulary)) +   # Add a point layer   geom_point()              

                ggplot(Vocab, aes(education, vocabulary)) +   # Change to a jitter layer   geom_jitter()              

                ggplot(Vocab, aes(education, vocabulary)) +   # Set the transparency to 0.2   geom_jitter(alpha=0.2)              

                ggplot(Vocab, aes(education, vocabulary)) +   # Set the shape to 1   geom_jitter(alpha = 0.2, shape = 1)              

Notice how jittering and alpha blending serves as a great solution to the overplotting problem here. Setting the shape to 1 didn't really help, but it was useful in the previous exercises when you had less data. We need to consider each plot individually.

Histograms

Histograms cut up a continuous variable into discrete bins and, by default, maps the internally calculated count variable (the number of observations in each bin) onto the y aesthetic. An internal variable called density can be accessed by using the .. notation, i.e. ..density… Plotting this variable will show the relative frequency, which is the height times the width of each bin.

              # Plot mpg ggplot(mtcars, aes(mpg)) +   # Add a histogram layer   geom_histogram()            

              ggplot(mtcars, aes(mpg)) +   # Set the binwidth to 1   geom_histogram(binwidth = 1)            

              # Map y to ..density.. ggplot(mtcars, aes(mpg, ..density..)) +   geom_histogram(binwidth = 1)            

              datacamp_light_blue <- "#51A8C9"  ggplot(mtcars, aes(mpg, ..density..)) +   # Set the fill color to datacamp_light_blue   geom_histogram(binwidth = 1, fill = datacamp_light_blue)            

Histograms are one of the most common exploratory plots for continuous data. If you want to use density on the y-axis be sure to set your binwidth to an intuitive value.

Positions in histograms

Here, we'll examine the various ways of applying positions to histograms. geom_histogram(), a special case of geom_bar(), has a position argument that can take on the following values:

  • stack (the default): Bars for different groups are stacked on top of each other.
  • dodge: Bars for different groups are placed side by side.
  • fill: Bars for different groups are shown as proportions.
  • identity: Plot the values as they appear in the dataset.
                # Update the aesthetics so the fill color is by fam ggplot(mtcars, aes(mpg, fill = fam)) +   geom_histogram(binwidth = 1)              

                ggplot(mtcars, aes(mpg, fill = fam)) +   # Change the position to dodge   geom_histogram(binwidth = 1, position = "dodge")              

                ggplot(mtcars, aes(mpg, fill = fam)) +   # Change the position to fill   geom_histogram(binwidth = 1, position = "fill")              

                ggplot(mtcars, aes(mpg, fill = fam)) +   # Change the position to identity, with transparency 0.4   geom_histogram(binwidth = 1, position = "identity", alpha = 0.4)              

Bar plots

Position in bar and col plots

Let's see how the position argument changes geom_bar().

We have three position options:

  • stack: The default
  • dodge: Preferred
  • fill: To show proportions

While we will be using geom_bar() here, note that the function geom_col() is just geom_bar() where both the position and stat arguments are set to "identity". It is used when we want the heights of the bars to represent the exact values in the data.

                # Plot fcyl, filled by fam ggplot(mtcars, aes(fcyl, fill = fam)) +   # Add a bar layer   geom_bar()              

                ggplot(mtcars, aes(fcyl, fill = fam)) +   # Set the position to "fill"   geom_bar(position = "fill")              

                ggplot(mtcars, aes(fcyl, fill = fam)) +   # Change the position to "dodge"   geom_bar(position = "dodge")              

Different kinds of plots need different position arguments, so it's important to be familiar with this attribute.

Overlapping bar plots

We can customize bar plots further by adjusting the dodging so that our bars partially overlap each other. Instead of using position = "dodge", we're going to use position_dodge(), like we did with position_jitter() in the the previously. Here, we'll save this as an object, posn_d, so that we can easily reuse it.

Remember, the reason we want to use position_dodge() (and position_jitter()) is to specify how much dodging (or jittering) you want.

                ggplot(mtcars, aes(cyl, fill = fam)) +   # Change position to use the functional form, with width 0.2   geom_bar(position = position_dodge(width = 0.2))              

                ggplot(mtcars, aes(cyl, fill = fam)) +   # Set the transparency to 0.6   geom_bar(position = position_dodge(width = 0.2), alpha = 0.6)              

By using these position functions, we can customize your plot to suit your needs.

Bar plots: sequential color palette

We'll fill each segment according to an ordinal variable. The best way to do that is with a sequential color palette.

                #example of using a sequential color palette ggplot(mtcars, aes(fcyl, fill = fam)) +   geom_bar() +   scale_fill_brewer(palette = "Set1")              

                Vocab = Vocab %>%    mutate(vocabulary = as.factor(vocabulary)) # Plot education, filled by vocabulary ggplot(Vocab, aes(education, fill = vocabulary)) +   geom_bar()              

                # Plot education, filled by vocabulary ggplot(Vocab, aes(education, fill = vocabulary)) +   # Add a bar layer with position "fill"   geom_bar(position = "fill")              

                # Plot education, filled by vocabulary ggplot(Vocab, aes(education, fill = vocabulary)) +   # Add a bar layer with position "fill"   geom_bar(position = "fill") +   # Add a brewer fill scale with default palette   scale_fill_brewer()              

Line plots

We'll use the economics dataset to make some line plots. The dataset contains a time series for unemployment and population statistics from the Federal Reserve Bank of St. Louis in the United States. The data is contained in the ggplot2 package.

To begin with, we can look at how the median unemployment time and the unemployment rate (the number of unemployed people as a proportion of the population) change over time.

              # Print the head of economics head(economics)            
                              # Using economics, plot unemploy vs. date ggplot(economics, aes(x = date, y = unemploy)) +   # Make it a line plot   geom_line()            

              # Change the y-axis to the proportion of the population that is unemployed ggplot(economics, aes(date, unemploy/pop)) +   geom_line()            

Multiple time series

We already saw how the form of your data affects how you can plot it. Let's explore that further with multiple time series. Here, it's important that all lines are on the same scale, and if possible, on the same plot.

fish.species contains the global capture rates of seven salmon species from 1950–2010. Each variable (column) is a Salmon species and each observation (row) is one year. fish.tidy contains the same data, but in three columns: Species, Year, and Capture (i.e. one variable per column).

                load("fish.RData")              
                library(tidyr) # Use gather to go from fish.species to fish.tidy fish.tidy <- gather(fish.species, Species, Capture, -Year)              
                str(fish.species)              
                'data.frame':   61 obs. of  8 variables:  $ Year    : int  1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 ...  $ Pink    : int  100600 259000 132600 235900 123400 244400 203400 270119 200798 200085 ...  $ Chum    : int  139300 155900 113800 99800 148700 143700 158480 125377 132407 113114 ...  $ Sockeye : int  64100 51200 58200 66100 83800 72000 84800 69676 100520 62472 ...  $ Coho    : int  30500 40900 33600 32400 38300 45100 40000 39900 39200 32865 ...  $ Rainbow : int  0 100 100 100 100 100 100 100 100 100 ...  $ Chinook : int  23200 25500 24900 25300 24500 27700 25300 21200 20900 20335 ...  $ Atlantic: int  10800 9701 9800 8800 9600 7800 8100 9000 8801 8700 ...              
                str(fish.tidy)              
                'data.frame':   427 obs. of  3 variables:  $ Year   : int  1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 ...  $ Species: chr  "Pink" "Pink" "Pink" "Pink" ...  $ Capture: int  100600 259000 132600 235900 123400 244400 203400 270119 200798 200085 ...              
                # Plot the Rainbow Salmon time series ggplot(fish.species, aes(x = Year, y = Rainbow)) +   geom_line()              

                # Plot the Pink Salmon time series ggplot(fish.species, aes(x = Year, y = Pink)) +   geom_line()              

                # Plot multiple time-series by grouping by species ggplot(fish.tidy, aes(Year, Capture)) +   geom_line(aes(group = Species))              

                # Plot multiple time-series by coloring by species ggplot(fish.tidy, aes(x = Year, y = Capture, color = Species)) +   geom_line(aes(group = Species))              

As we can see in the the last couple of plots, a grouping aesthetic was vital here. If you don't specify color = Species, you'll get a mess of lines.

Themes

Moving the legend

To change stylistic elements of a plot, call theme() and set plot properties to a new value. For example, the following changes the legend position.

              p + theme(legend.position = new_value)            

Here, the new value can be

  • "top", "bottom", "left", or "right'": place it at that side of the plot.
  • "none": don't draw it.
  • c(x, y): c(0, 0) means the bottom-left and c(1, 1) means the top-right.
              recess <- data.frame(   begin = c("1969-12-01","1973-11-01","1980-01-01","1981-07-01","1990-07-01","2001-03-01", "2007-12-01"),    end = c("1970-11-01","1975-03-01","1980-07-01","1982-11-01","1991-03-01","2001-11-01", "2009-07-30"),   event = c("Fiscal & Monetary\ntightening", "1973 Oil crisis", "Double dip I","Double dip II", "Oil price shock", "Dot-com bubble", "Sub-prime\nmortgage crisis"),   y =  c(.01415981, 0.02067402, 0.02951190,  0.03419201,  0.02767339, 0.02159662,0.02520715),   stringsAsFactors = F   )  library(lubridate)            
                              Attaching package: 㤼㸱lubridate㤼㸲  The following object is masked from 㤼㸱package:base㤼㸲:      date            
              recess$begin <- ymd (recess$begin) recess$end <- ymd (recess$end)            
              plt_prop_unemployed_over_time = ggplot(economics, aes(x = date, y = unemploy/pop)) +   ggtitle(c("The percentage of unemployed Americans \n increases sharply during recessions")) +   geom_line() +   geom_rect(data = recess,              aes(xmin = begin, xmax = end, ymin = -Inf, ymax = +Inf, fill = "Recession"),              inherit.aes = FALSE, alpha = 0.2) +   geom_label(data = recess, aes(x = end, y = y, label=event), size = 3) +      scale_fill_manual(name = "", values="red", label="Recessions")  plt_prop_unemployed_over_time            

              # View the default plot plt_prop_unemployed_over_time            

                              # Remove legend entirely plt_prop_unemployed_over_time +   theme(legend.position = "none")            

              # Position the legend at the bottom of the plot plt_prop_unemployed_over_time +   theme(legend.position = "bottom")            

              # Position the legend inside the plot at (0.6, 0.1) plt_prop_unemployed_over_time +   theme(legend.position = c(0.6,0.1))            

But be careful when placing a legend inside your plotting space. You could end up obscuring data.

Modifying theme elements

Many plot elements have multiple properties that can be set. For example, line elements in the plot such as axes and gridlines have a color, a thickness (size), and a line type (solid line, dashed, or dotted). To set the style of a line, you use element_line(). For example, to make the axis lines into red, dashed lines, you would use the following.

              p + theme(axis.line = element_line(color = "red", linetype = "dashed"))            

Similarly, element_rect() changes rectangles and element_text() changes text. You can remove a plot element using element_blank().

              plt_prop_unemployed_over_time +   theme(     # For all rectangles, set the fill color to grey92     rect = element_rect(fill = "grey92"),     # For the legend key, turn off the outline     legend.key = element_rect(color = NA)   )            

              plt_prop_unemployed_over_time +   theme(     rect = element_rect(fill = "grey92"),     legend.key = element_rect(color = NA),     # Turn off axis ticks     axis.ticks = element_blank(),     # Turn off the panel grid     panel.grid = element_blank()   )            

              plt_prop_unemployed_over_time +   theme(     rect = element_rect(fill = "grey92"),     legend.key = element_rect(color = NA),     axis.ticks = element_blank(),     panel.grid = element_blank(),     # Add major y-axis panel grid lines back     panel.grid.major.y = element_line(       # Set the color to white       color = "white",       # Set the size to 0.5       size = 0.5,       # Set the line type to dotted       linetype = "dotted"     )   )            

              plt_prop_unemployed_over_time +   theme(     rect = element_rect(fill = "grey92"),     legend.key = element_rect(color = NA),     axis.ticks = element_blank(),     panel.grid = element_blank(),     panel.grid.major.y = element_line(       color = "white",       size = 0.5,       linetype = "dotted"     ),     # Set the axis text color to grey25     axis.text = element_text(color ="grey25"),     # Set the plot title font face to italic and font size to 16    plot.title = element_text(size = 16, face = "italic")   )            

Excellent Explanatory Plot! This plot is ready for prime time – it's pretty AND informative. Make sure that all your text is legible for the context in which it will be viewed.

Modifying whitespace

Whitespace means all the non-visible margins and spacing in the plot.

To set a single whitespace value, use unit(x, unit), where x is the amount and unit is the unit of measure.

Borders require you to set 4 positions, so use margin(top, right, bottom, left, unit). To remember the margin order, think TRouBLe.

The default unit is "pt" (points), which scales well with text. Other options include "cm", "in" (inches) and "lines" (of text).

              # View the original plot plt_mpg_vs_wt_by_cyl <- ggplot(mtcars, aes(wt, mpg, color = fcyl)) +   ylab("Miels per gallon") +    xlab("weight (1000/lbs)") +   geom_point() plt_mpg_vs_wt_by_cyl            

              plt_mpg_vs_wt_by_cyl +   theme(     # Set the axis tick length to 2 lines     axis.ticks.length = unit(2, "lines")   )            

              plt_mpg_vs_wt_by_cyl +   theme(     # Set the legend key size to 3 centimeters     legend.key.size = unit(3, "cm")   )            

              plt_mpg_vs_wt_by_cyl +   theme(     # Set the legend margin to (20, 30, 40, 50) points     legend.margin = margin(20, 30, 40, 50, "pt")   )            

              plt_mpg_vs_wt_by_cyl +   theme(     # Set the plot margin to (10, 30, 50, 70) millimeters     plot.margin = margin(10, 30, 50, 70, "mm")   )            

Changing the whitespace can be useful if you need to make your plot more compact, or if you want to create more space to reduce "business".

Built-in Themes

Built-in themes In addition to making your own themes, there are several out-of-the-box solutions that may save you lots of time.

  • theme_gray() is the default.
  • theme_bw() is useful when you use transparency.
  • theme_classic() is more traditional.
  • theme_void() removes everything but the data.
              # Add a black and white theme plt_prop_unemployed_over_time +   theme_bw()            

              # Add a classic theme plt_prop_unemployed_over_time +   theme_classic()            

              # Add a void theme plt_prop_unemployed_over_time +   theme_void()            

The black and white theme works really well if you use transparency in your plot.

Exploring ggthemes package

Outside of ggplot2, another source of built-in themes is the ggthemes package.

              library(ggthemes)            
              package 㤼㸱ggthemes㤼㸲 was built under R version 3.6.3            
              # Use the fivethirtyeight theme plt_prop_unemployed_over_time +   theme_fivethirtyeight()            

              # Use Tufte's theme plt_prop_unemployed_over_time +   theme_tufte()            

              # Use the Wall Street Journal theme plt_prop_unemployed_over_time +   theme_wsj()            

ggthemes has over 20 themes for you to try.

Setting themes

Reusing a theme across many plots helps to provide a consistent style. You have several options for this.

  1. Assign the theme to a variable, and add it to each plot.
  2. Set your theme as the default using theme_set().

A good strategy that we'll use here is to begin with a built-in theme then modify it.

              # Save the theme as theme_recession theme_recession <- theme(   rect = element_rect(fill = "grey92"),   legend.key = element_rect(color = NA),   axis.ticks = element_blank(),   panel.grid = element_blank(),   panel.grid.major.y = element_line(color = "white", size = 0.5, linetype = "dotted"),   axis.text = element_text(color = "grey25"),   plot.title = element_text(face = "italic", size = 16),   legend.position = c(0.6, 0.1) )  # Combine the Tufte theme with theme_recession theme_tufte_recession <- theme_tufte() + theme_recession  # Add the Tufte recession theme to the plot plt_prop_unemployed_over_time + theme_tufte_recession            

              theme_recession <- theme(   rect = element_rect(fill = "grey92"),   legend.key = element_rect(color = NA),   axis.ticks = element_blank(),   panel.grid = element_blank(),   panel.grid.major.y = element_line(color = "white", size = 0.5, linetype = "dotted"),   axis.text = element_text(color = "grey25"),   plot.title = element_text(face = "italic", size = 16),   legend.position = c(0.6, 0.1) ) theme_tufte_recession <- theme_tufte() + theme_recession  # Set theme_tufte_recession as the default theme theme_set(theme_tufte_recession)   # Draw the plot (without explicitly adding a theme) plt_prop_unemployed_over_time            

Publication-quality plots

              plt_prop_unemployed_over_time +   # Add Tufte's theme   theme_tufte()            

              plt_prop_unemployed_over_time +   theme_tufte() +   # Add individual theme elements   theme(     # Turn off the legend     legend.position = "none",     # Turn off the axis ticks     axis.ticks = element_blank()   )            

              plt_prop_unemployed_over_time +   theme_tufte() +   theme(     legend.position = "none",     axis.ticks = element_blank(),     # Set the axis title's text color to grey60     axis.title = element_text(color = "grey60"),     # Set the axis text's text color to grey60     axis.text = element_text(color = "grey60")   )            

              plt_prop_unemployed_over_time +   theme_tufte() +   theme(     legend.position = "none",     axis.ticks = element_blank(),     axis.title = element_text(color = "grey60"),     axis.text = element_text(color = "grey60"),     # Set the panel gridlines major y values     panel.grid.major.y = element_line(       # Set the color to grey60       color = "grey60",       # Set the size to 0.25       size = 0.25,       # Set the linetype to dotted       linetype = "dotted"     )   )            

Using geoms for explanatory plots

Let's focus on producing beautiful and effective explanatory plots. In the next couple of exercises, we'll create a plot that is similar to the one shown in the video using gm2007, a filtered subset of the gapminder dataset.

This type of plot will be in an info-viz style, meaning that it would be similar to something you'd see in a magazine or website for a mostly lay audience.

              library(gapminder)            
              package 㤼㸱gapminder㤼㸲 was built under R version 3.6.3            
              gapminder            
              gm2007 <- gapminder %>%    filter(year == 2007) %>%    select(country, lifeExp, continent) %>%  filter(lifeExp > 80.6 | lifeExp <46) %>%    arrange(lifeExp) gm2007            
              gm2007_full <- gapminder %>%    filter(year == 2007) %>%    select(country, lifeExp, continent)            
              # Add a geom_segment() layer ggplot(gm2007, aes(x = lifeExp, y = country, color = lifeExp)) +   geom_point(size = 4) +   geom_segment(aes(xend = 30, yend = country), size = 2) +   theme(legend.position="right")            

              # Add a geom_text() layer ggplot(gm2007, aes(x = lifeExp, y = country, color = lifeExp)) +   geom_point(size = 4) +   geom_segment(aes(xend = 30, yend = country), size = 2) +   geom_text(aes(label = lifeExp), color = "white", size = 1.5) +   theme(legend.position="right")            

              library(RColorBrewer) # Set the color scale palette <- brewer.pal(5, "RdYlBu")[-(2:4)]  # Modify the scales ggplot(gm2007, aes(x = lifeExp, y = country, color = lifeExp)) +   geom_point(size = 4) +   geom_segment(aes(xend = 30, yend = country), size = 2) +   geom_text(aes(label = round(lifeExp,1)), color = "white", size = 1.5) +   scale_x_continuous("", expand = c(0, 0), limits = c(30,90), position = "top") +   scale_color_gradientn(colors = palette) +   theme(legend.position="right")            

              # Set the color scale palette <- brewer.pal(5, "RdYlBu")[-(2:4)]  # Add a title and caption plt_country_vs_lifeExp <- ggplot(gm2007, aes(x = lifeExp, y = country, color = lifeExp)) +   geom_point(size = 4) +   geom_segment(aes(xend = 30, yend = country), size = 2) +   geom_text(aes(label = round(lifeExp,1)), color = "white", size = 1.5) +   scale_x_continuous("", expand = c(0,0), limits = c(30,90), position = "top") +   scale_color_gradientn(colors = palette) +   labs(title = "Highest and lowest life expectancies, 2007", caption = "Source: gapminder") +   theme(legend.position="right") plt_country_vs_lifeExp            

Using annotate() for embellishments

We completed our basic plot. Now let's polish it by playing with the theme and adding annotations. In this exercise, we'll use annotate() to add text and a curve to the plot.

The following values have been calculated for you to assist with adding embellishments to the plot:

              global_mean <- mean(gm2007_full$lifeExp) x_start <- global_mean + 4 y_start <- 5.5 x_end <- global_mean y_end <- 7.5            
              # Define the theme plt_country_vs_lifeExp <- plt_country_vs_lifeExp +   theme_classic() +   theme(axis.line.y = element_blank(),         axis.ticks.y = element_blank(),         axis.text = element_text(color = "black"),         axis.title = element_blank(),         legend.position = "none") plt_country_vs_lifeExp            

              # Add a vertical line plt_country_vs_lifeExp <- plt_country_vs_lifeExp +   geom_vline(xintercept = global_mean, color = "grey40", linetype = 3) plt_country_vs_lifeExp            

              plt_country_vs_lifeExp <- plt_country_vs_lifeExp  +   annotate(     "text",     x = x_start, y = y_start,     label = "The\nglobal\naverage",     vjust = 1, size = 3, color = "grey40"   ) plt_country_vs_lifeExp            

              plt_country_vs_lifeExp <- plt_country_vs_lifeExp  +   annotate(     "curve",     x = x_start, y = y_start,     xend = x_end, yend = y_end,     arrow = arrow(length = unit(0.2, "cm"), type = "closed"),     color = "grey40"   ) plt_country_vs_lifeExp            

Your explanatory plot clearly shows the countries with the highest and lowest life expectancy and would be great for a lay audience.

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90MiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhlIG10Y2FycyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIDMyIGNhcnMgZnJvbSBhIDE5NzMgaXNzdWUgb2YgTW90b3IgVHJlbmQgbWFnYXppbmUuIFRoaXMgZGF0YXNldCBpcyBzbWFsbCwgaW50dWl0aXZlLCBhbmQgY29udGFpbnMgYSB2YXJpZXR5IG9mIGNvbnRpbnVvdXMgYW5kIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4NCg0KYGBge3J9DQojIExvYWQgdGhlIGdncGxvdDIgcGFja2FnZQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCiMgRXhwbG9yZSB0aGUgbXRjYXJzIGRhdGEgZnJhbWUgd2l0aCBzdHIoKQ0Kc3RyKG10Y2FycykNCg0KIyBFeGVjdXRlIHRoZSBmb2xsb3dpbmcgY29tbWFuZA0KZ2dwbG90KG10Y2FycywgYWVzKGN5bCwgbXBnKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KTm90aWNlIHRoYXQgZ2dwbG90MiB0cmVhdHMgY3lsIGFzIGEgY29udGludW91cyB2YXJpYWJsZS4gV2UgZ2V0IGEgcGxvdCwgYnV0IGl0J3Mgbm90IHF1aXRlIHJpZ2h0LCBiZWNhdXNlIGl0IGdpdmVzIHRoZSBpbXByZXNzaW9uIHRoYXQgdGhlcmUgaXMgc3VjaCBhIHRoaW5nIGFzIGEgNSBvciA3LWN5bGluZGVyIGNhciwgd2hpY2ggdGhlcmUgaXMgbm90Lg0KDQojIyBEYXRhIGNvbHVtbnMgdHlwZXMgYWZmZWN0IHBsb3QgdHlwZXMNCg0KQWx0aG91Z2ggY3lsICh0aGUgbnVtYmVyIG9mIGN5bGluZGVycykgaXMgY2F0ZWdvcmljYWwsIHlvdSBwcm9iYWJseSBub3RpY2VkIHRoYXQgaXQgaXMgY2xhc3NpZmllZCBhcyBudW1lcmljIGluIG10Y2Fycy4gVGhpcyBpcyByZWFsbHkgbWlzbGVhZGluZyBiZWNhdXNlIHRoZSByZXByZXNlbnRhdGlvbiBpbiB0aGUgcGxvdCBkb2Vzbid0IG1hdGNoIHRoZSBhY3R1YWwgZGF0YSB0eXBlLiBZb3UnbGwgaGF2ZSB0byBleHBsaWNpdGx5IHRlbGwgZ2dwbG90MiB0aGF0IGN5bCBpcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLg0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgZ2dwbG90MiBwYWNrYWdlDQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQ2hhbmdlIHRoZSBjb21tYW5kIGJlbG93IHNvIHRoYXQgY3lsIGlzIHRyZWF0ZWQgYXMgZmFjdG9yDQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCk5vdGljZSB0aGF0IGdncGxvdDIgdHJlYXRzIGN5bCBhcyBhIGZhY3Rvci4gVGhpcyB0aW1lIHRoZSB4LWF4aXMgZG9lcyBub3QgY29udGFpbiB2YXJpYWJsZXMgbGlrZSA1IG9yIDcsIG9ubHkgdGhlIHZhbHVlcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBkYXRhc2V0Lg0KDQojIyBUaGUgZ3JhbW1hciBvZiBncmFwaGljcw0KDQojIyMgTWFwcGluZyBkYXRhIGNvbHVtbnMgdG8gYWVzdGhldGljcw0KDQpMZXQncyBkaXZlIGEgbGl0dGxlIGRlZXBlciBpbnRvIHRoZSB0aHJlZSBtYWluIHRvcGljcyBpbiB0aGlzIGNvdXJzZTogVGhlIGRhdGEsIGFlc3RoZXRpY3MsIGFuZCBnZW9tIGxheWVycy4gDQoNCmBgYHtyfQ0KIyBFZGl0IHRvIGFkZCBhIGNvbG9yIGFlc3RoZXRpYyBtYXBwZWQgdG8gZGlzcA0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcsIGNvbG9yID0gZGlzcCkpICsNCiAgZ2VvbV9wb2ludCgpDQojIENoYW5nZSB0aGUgY29sb3IgYWVzdGhldGljIHRvIGEgc2l6ZSBhZXN0aGV0aWMNCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnLCBzaXplID0gZGlzcCkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCiMjIyBVbmRlcnN0YW5kaW5nIHZhcmlhYmxlcw0KDQpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgeW91IHNhdyB0aGF0IGRpc3AgY2FuIGJlIG1hcHBlZCBvbnRvIGEgY29sb3IgZ3JhZGllbnQgb3Igb250byBhIGNvbnRpbnVvdXMgc2l6ZSBzY2FsZS4NCg0KQW5vdGhlciBhcmd1bWVudCBvZiBhZXMoKSBpcyB0aGUgc2hhcGUgb2YgdGhlIHBvaW50cy4gVGhlcmUgYXJlIGEgZmluaXRlIG51bWJlciBvZiBzaGFwZXMgd2hpY2ggZ2dwbG90KCkgY2FuIGF1dG9tYXRpY2FsbHkgYXNzaWduIHRvIHRoZSBwb2ludHMuDQoNCiMjIGdncGxvdDIgbGF5ZXJzDQoNCiMjIyBBZGRpbmcgZ2VvbWV0cmllcw0KDQpUaGUgZGlhbW9uZHMgZGF0YXNldCBjb250YWlucyBkZXRhaWxzIG9mIDEsMDAwIGRpYW1vbmRzLiBBbW9uZyB0aGUgdmFyaWFibGVzIGluY2x1ZGVkIGFyZSBjYXJhdCAoYSBtZWFzdXJlbWVudCBvZiB0aGUgZGlhbW9uZCdzIHNpemUpIGFuZCBwcmljZS4NCg0KV2UnbGwgdXNlIHR3byBjb21tb24gZ2VvbSBsYXllciBmdW5jdGlvbnM6DQotIGdlb21fcG9pbnQoKSBhZGRzIHBvaW50cyAoYXMgaW4gYSBzY2F0dGVyIHBsb3QpLg0KLSBnZW9tX3Ntb290aCgpIGFkZHMgYSBzbW9vdGggdHJlbmQgY3VydmUuDQpgYGB7cn0NCiMgRXhwbG9yZSB0aGUgZGlhbW9uZHMgZGF0YSBmcmFtZSB3aXRoIHN0cigpDQpzdHIoZGlhbW9uZHMpDQpgYGANCmBgYHtyfQ0KIyBBZGQgZ2VvbV9wb2ludCgpIHdpdGggKw0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoY2FyYXQsIHByaWNlKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KYGBge3J9DQojIEFkZCBnZW9tX3Ntb290aCgpIHdpdGggKw0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoY2FyYXQsIHByaWNlKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpDQpgYGANCiMjIyBDaGFuZ2luZyBvbmUgZ2VvbSBvciBldmVyeSBnZW9tDQoNCklmIHdlIGhhdmUgbXVsdGlwbGUgZ2VvbXMsIHRoZW4gbWFwcGluZyBhbiBhZXN0aGV0aWMgdG8gZGF0YSB2YXJpYWJsZSBpbnNpZGUgdGhlIGNhbGwgdG8gZ2dwbG90KCkgd2lsbCBjaGFuZ2UgYWxsIHRoZSBnZW9tcy4gSXQgaXMgYWxzbyBwb3NzaWJsZSB0byBtYWtlIGNoYW5nZXMgdG8gaW5kaXZpZHVhbCBnZW9tcyBieSBwYXNzaW5nIGFyZ3VtZW50cyB0byB0aGUgZ2VvbV8qKCkgZnVuY3Rpb25zLg0KDQpnZW9tX3BvaW50KCkgaGFzIGFuIGFscGhhIGFyZ3VtZW50IHRoYXQgY29udHJvbHMgdGhlIG9wYWNpdHkgb2YgdGhlIHBvaW50cy4gQSB2YWx1ZSBvZiAxICh0aGUgZGVmYXVsdCkgbWVhbnMgdGhhdCB0aGUgcG9pbnRzIGFyZSB0b3RhbGx5IG9wYXF1ZTsgYSB2YWx1ZSBvZiAwIG1lYW5zIHRoZSBwb2ludHMgYXJlIHRvdGFsbHkgdHJhbnNwYXJlbnQgKGFuZCB0aGVyZWZvcmUgaW52aXNpYmxlKS4gVmFsdWVzIGluIGJldHdlZW4gc3BlY2lmeSB0cmFuc3BhcmVuY3kuDQpgYGB7cn0NCiMgTWFwIHRoZSBjb2xvciBhZXN0aGV0aWMgdG8gY2xhcml0eQ0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoY2FyYXQsIHByaWNlLCBjb2xvciA9IGNsYXJpdHkpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkNCmBgYA0KYGBge3J9DQojIE1ha2UgdGhlIHBvaW50cyA0MCUgb3BhcXVlDQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyhjYXJhdCwgcHJpY2UsIGNvbG9yID0gY2xhcml0eSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNCkgKw0KICBnZW9tX3Ntb290aCgpDQpgYGANCiMjIyBTYXZpbmcgcGxvdHMgYXMgdmFyaWFibGVzDQoNClBsb3RzIGNhbiBiZSBzYXZlZCBhcyB2YXJpYWJsZXMsIHdoaWNoIGNhbiBiZSBhZGRlZCB0d28gbGF0ZXIgb24gdXNpbmcgdGhlICsgb3BlcmF0b3IuIFRoaXMgaXMgcmVhbGx5IHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBtYWtlIG11bHRpcGxlIHJlbGF0ZWQgcGxvdHMgZnJvbSBhIGNvbW1vbiBiYXNlLg0KDQpgYGB7cn0NCiMgRHJhdyBhIGdncGxvdA0KcGx0X3ByaWNlX3ZzX2NhcmF0IDwtIGdncGxvdCgNCiAgIyBVc2UgdGhlIGRpYW1vbmRzIGRhdGFzZXQNCiAgZGlhbW9uZHMsDQogICMgRm9yIHRoZSBhZXN0aGV0aWNzLCBtYXAgeCB0byBjYXJhdCBhbmQgeSB0byBwcmljZQ0KICBhZXMoY2FyYXQsIHByaWNlKQ0KKQ0KDQojIEFkZCBhIHBvaW50IGxheWVyIHRvIHBsdF9wcmljZV92c19jYXJhdA0KcGx0X3ByaWNlX3ZzX2NhcmF0ICsgZ2VvbV9wb2ludCgpDQpgYGANCmBgYHtyfQ0KIyBFZGl0IHRoaXMgdG8gbWFrZSBwb2ludHMgMjAlIG9wYXF1ZTogcGx0X3ByaWNlX3ZzX2NhcmF0X3RyYW5zcGFyZW50DQpwbHRfcHJpY2VfdnNfY2FyYXRfdHJhbnNwYXJlbnQgPC0gcGx0X3ByaWNlX3ZzX2NhcmF0ICsgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikNCg0KIyBTZWUgdGhlIHBsb3QNCnBsdF9wcmljZV92c19jYXJhdF90cmFuc3BhcmVudA0KYGBgDQpgYGB7cn0NCiMgRWRpdCB0aGlzIHRvIG1hcCBjb2xvciB0byBjbGFyaXR5LA0KIyBBc3NpZ24gdGhlIHVwZGF0ZWQgcGxvdCB0byBhIG5ldyBvYmplY3QNCnBsdF9wcmljZV92c19jYXJhdF9ieV9jbGFyaXR5IDwtIHBsdF9wcmljZV92c19jYXJhdCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhcml0eSkpDQoNCiMgU2VlIHRoZSBwbG90DQpwbHRfcHJpY2VfdnNfY2FyYXRfYnlfY2xhcml0eQ0KYGBgDQpCeSBhc3NpZ25pbmcgcGFydHMgb2YgcGxvdHMgdG8gYSB2YXJpYWJsZSB0aGVuIHJldXNpbmcgdGhhdCB2YXJpYWJsZSBpbiBvdGhlciBwbG90cywgaXQgbWFrZXMgaXQgcmVhbGx5IGNsZWFyIGhvdyBtdWNoIHRob3NlIHBsb3RzIGhhdmUgaW4gY29tbW9uLg0KDQoNCiMgQWVzdGhldGljcw0KDQojIyBDb2xvciwgc2hhcGUgYW5kIHNpemUNCg0KVGhlc2UgYXJlIHRoZSBhZXN0aGV0aWNzIHdlIGNhbiBjb25zaWRlciB3aXRoaW4gYWVzKCkgaW4gdGhpcyBjaGFwdGVyOiB4LCB5LCBjb2xvciwgZmlsbCwgc2l6ZSwgYWxwaGEsIGxhYmVscyBhbmQgc2hhcGUuDQoNCk9uZSBjb21tb24gY29udmVudGlvbiBpcyB0aGF0IHlvdSBkb24ndCBuYW1lIHRoZSB4IGFuZCB5IGFyZ3VtZW50cyB0byBhZXMoKSwgc2luY2UgdGhleSBhbG1vc3QgYWx3YXlzIGNvbWUgZmlyc3QsIGJ1dCB5b3UgZG8gbmFtZSBvdGhlciBhcmd1bWVudHMuDQpgYGB7cn0NCiNjcmVhdGUgZmFjdG9yIGZjeWwNCm10Y2FycyA8LSBtdGNhcnMgJT4lIA0KICBtdXRhdGUoZmN5bCA9IGFzLmZhY3RvcihjeWwpLA0KICAgICAgICAgZmFtID0gYXMuZmFjdG9yKGFtKSkNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JjYXRzKQ0KbXRjYXJzIDwtIG10Y2FycyAlPiUgDQogIG11dGF0ZShmYW0gPSBmY3RfcmVjb2RlKGZhbSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIm1hbnVhbCIgPSAiMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgImF1dG9tYXRpYyIgPSAiMCIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBNYXAgeCB0byBtcGcgYW5kIHkgdG8gZmN5bA0KZ2dwbG90KG10Y2FycywgYWVzKG1wZywgZmN5bCkpICsNCiAgZ2VvbV9wb2ludCgpDQoNCmBgYA0KYGBge3J9DQojIFN3YXAgbXBnIGFuZCBmY3lsDQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmN5bCwgbXBnKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KYGBge3J9DQojIE1hcCB4IHRvIHd0LCB5IHRvIG1wZyBhbmQgY29sb3IgdG8gZmN5bA0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcsIGNvbG9yID0gZmN5bCkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcsIGNvbG9yID0gZmN5bCkpICsNCiAgIyBTZXQgdGhlIHNoYXBlIGFuZCBzaXplIG9mIHRoZSBwb2ludHMNCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIHNpemUgPSA0KQ0KYGBgDQojIyBDb2xvciB2cy4gZmlsbA0KDQpUeXBpY2FsbHksIHRoZSBjb2xvciBhZXN0aGV0aWMgY2hhbmdlcyB0aGUgb3V0bGluZSBvZiBhIGdlb20gYW5kIHRoZSBmaWxsIGFlc3RoZXRpYyBjaGFuZ2VzIHRoZSBpbnNpZGUuIGdlb21fcG9pbnQoKSBpcyBhbiBleGNlcHRpb246IHlvdSB1c2UgY29sb3IgKG5vdCBmaWxsKSBmb3IgdGhlIHBvaW50IGNvbG9yLiBIb3dldmVyLCBzb21lIHNoYXBlcyBoYXZlIHNwZWNpYWwgYmVoYXZpb3IuDQoNClRoZSBkZWZhdWx0IGdlb21fcG9pbnQoKSB1c2VzIHNoYXBlID0gMTk6IGEgc29saWQgY2lyY2xlLiBBbiBhbHRlcm5hdGl2ZSBpcyBzaGFwZSA9IDIxOiBhIGNpcmNsZSB0aGF0IGFsbG93IHlvdSB0byB1c2UgYm90aCBmaWxsIGZvciB0aGUgaW5zaWRlIGFuZCBjb2xvciBmb3IgdGhlIG91dGxpbmUuIFRoaXMgaXMgbGV0cyB5b3UgdG8gbWFwIHR3byBhZXN0aGV0aWNzIHRvIGVhY2ggcG9pbnQuDQoNCkFsbCBzaGFwZSB2YWx1ZXMgYXJlIGRlc2NyaWJlZCBvbiB0aGUgcG9pbnRzKCkgaGVscCBwYWdlLg0KYGBge3J9DQojIE1hcCBmY3lsIHRvIGZpbGwNCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnLCBmaWxsID0gZmN5bCkpICsNCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIHNpemUgPSA0KQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnLCBmaWxsID0gZmN5bCkpICsNCiAgIyBDaGFuZ2UgcG9pbnQgc2hhcGU7IHNldCBhbHBoYQ0KICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIHNpemUgPSA0LCBhbHBoYSA9IDAuNikNCmBgYA0KYGBge3J9DQojIE1hcCBjb2xvciB0byBmYW0NCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnLCBmaWxsID0gZmN5bCwgY29sb3IgPSBmYW0pKSArDQogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgc2l6ZSA9IDQsIGFscGhhID0gMC42KQ0KYGBgDQpOb3RpY2UgdGhhdCBtYXBwaW5nIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgb250byBmaWxsIGRvZXNuJ3QgY2hhbmdlIHRoZSBjb2xvcnMsIGFsdGhvdWdoIGEgbGVnZW5kIGlzIGdlbmVyYXRlZCEgVGhpcyBpcyBiZWNhdXNlIHRoZSBkZWZhdWx0IHNoYXBlIGZvciBwb2ludHMgb25seSBoYXMgYSBjb2xvciBhdHRyaWJ1dGUgYW5kIG5vdCBhIGZpbGwgYXR0cmlidXRlISBVc2UgZmlsbCB3aGVuIHlvdSBoYXZlIGFub3RoZXIgc2hhcGUgKHN1Y2ggYXMgYSBiYXIpLCBvciB3aGVuIHVzaW5nIGEgcG9pbnQgdGhhdCBkb2VzIGhhdmUgYSBmaWxsIGFuZCBhIGNvbG9yIGF0dHJpYnV0ZSwgc3VjaCBhcyBzaGFwZSA9IDIxLCB3aGljaCBpcyBhIGNpcmNsZSB3aXRoIGFuIG91dGxpbmUuIEFueSB0aW1lIHlvdSB1c2UgYSBzb2xpZCBjb2xvciwgbWFrZSBzdXJlIHRvIHVzZSBhbHBoYSBibGVuZGluZyB0byBhY2NvdW50IGZvciBvdmVyIHBsb3R0aW5nLg0KDQojIyBDb21wYXJpbmcgYWVzdGhldGljcw0KDQpCZSBjYXJlZnVsIG9mIGEgbWFqb3IgcGl0ZmFsbDogdGhlc2UgYXR0cmlidXRlcyBjYW4gb3ZlcndyaXRlIHRoZSBhZXN0aGV0aWNzIG9mIHlvdXIgcGxvdCENCg0KYGBge3J9DQojIEVzdGFibGlzaCB0aGUgYmFzZSBsYXllcg0KcGx0X21wZ192c193dCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpDQpgYGANCmBgYHtyfQ0KIyBNYXAgZmN5bCB0byBzaXplDQpwbHRfbXBnX3ZzX3d0ICsNCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGZjeWwpKQ0KYGBgDQpgYGB7cn0NCnBsdF9tcGdfdnNfd3QgKw0KICBnZW9tX3BvaW50KGFlcyhhbHBoYSA9IGZjeWwpKQ0KYGBgDQpgYGB7cn0NCiMgTWFwIGZjeWwgdG8gc2hhcGUsIG5vdCBhbHBoYQ0KcGx0X21wZ192c193dCArDQogIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gZmN5bCkpDQpgYGANCmBgYHtyfQ0KIyBVc2UgdGV4dCBsYXllciBhbmQgbWFwIGZjeWwgdG8gbGFiZWwNCnBsdF9tcGdfdnNfd3QgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZmN5bCkpDQpgYGANCkxhYmVsIGFuZCBzaGFwZSBhcmUgb25seSBhcHBsaWNhYmxlIHRvIGNhdGVnb3JpY2FsIGRhdGEuDQoNCiMjIENvbG9yLCBzaGFwZSwgc2l6ZSBhbmQgYWxwaGENCg0KVGhpcyB0aW1lIHdlJ2xsIHVzZSB0aGVzZSBhcmd1bWVudHMgdG8gc2V0IGF0dHJpYnV0ZXMgb2YgdGhlIHBsb3QsIG5vdCBtYXAgdmFyaWFibGVzIG9udG8gYWVzdGhldGljcy4NCg0KV2UgY2FuIHNwZWNpZnkgY29sb3JzIGluIFIgdXNpbmcgaGV4IGNvZGVzOiBhIGhhc2ggZm9sbG93ZWQgYnkgdHdvIGhleGFkZWNpbWFsIG51bWJlcnMgZWFjaCBmb3IgcmVkLCBncmVlbiwgYW5kIGJsdWUgKCIjUlJHR0JCIikuIEhleGFkZWNpbWFsIGlzIGJhc2UtMTYgY291bnRpbmcuIFdlIGhhdmUgMCB0byA5LCBhbmQgQSByZXByZXNlbnRpbmcgMTAgdXAgdG8gRiByZXByZXNlbnRpbmcgMTUuIFBhaXJzIG9mIGhleGFkZWNpbWFsIG51bWJlcnMgZ2l2ZSB5b3UgYSByYW5nZSBmcm9tIDAgdG8gMjU1LiAiIzAwMDAwMCIgaXMgImJsYWNrIiAobm8gY29sb3IpLCAiI0ZGRkZGRiIgbWVhbnMgIndoaXRlIiwgYW5kIGAiIzAwRkZGRiIgaXMgY3lhbiAobWl4ZWQgZ3JlZW4gYW5kIGJsdWUpLg0KYGBge3J9DQojIEEgaGV4YWRlY2ltYWwgY29sb3INCm15X2JsdWUgPC0gIiM0QUJFRkYiDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArDQogICMgU2V0IHRoZSBwb2ludCBjb2xvciBhbmQgYWxwaGENCiAgZ2VvbV9wb2ludChjb2xvciA9IG15X2JsdWUsIGFscGhhID0gMC42KQ0KYGBgDQpgYGB7cn0NCiMgQ2hhbmdlIHRoZSBjb2xvciBtYXBwaW5nIHRvIGEgZmlsbCBtYXBwaW5nDQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZywgZmlsbCA9IGZjeWwpKSArDQogICMgU2V0IHBvaW50IHNpemUgYW5kIHNoYXBlDQogIGdlb21fcG9pbnQoY29sb3IgPSBteV9ibHVlLCBzaXplID0gMTAsIHNoYXBlID0gMSkNCmBgYA0KZ2dwbG90MiBsZXRzIHlvdSBjb250cm9sIHRoZXNlIGF0dHJpYnV0ZXMgaW4gbWFueSB3YXlzIHRvIGN1c3RvbWl6ZSB5b3VyIHBsb3RzLg0KDQojIyBDb25mbGljdHMgd2l0aCBhZXN0aGV0aWNzDQoNCldlIGNhbiB1c2UgYWxsIHRoZSBhZXN0aGV0aWNzIGFzIGF0dHJpYnV0ZXMuIExldCdzIHNlZSBob3cgdGhpcyB3b3JrcyB3aXRoIHRoZSBhZXN0aGV0aWNzIHlvdSB1c2VkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZXM6IHgsIHksIGNvbG9yLCBmaWxsLCBzaXplLCBhbHBoYSwgbGFiZWwgYW5kIHNoYXBlLg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZywgY29sb3IgPSBmY3lsKSkgKw0KICAjIEFkZCBwb2ludCBsYXllciB3aXRoIGFscGhhIDAuNQ0KICBnZW9tX3BvaW50KGFscGhhID0gMC41KQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnLCBjb2xvciA9IGZjeWwpKSArDQogICMgQWRkIHRleHQgbGF5ZXIgd2l0aCBsYWJlbCByb3duYW1lcyhtdGNhcnMpIGFuZCBjb2xvciByZWQNCiAgZ2VvbV90ZXh0KGxhYmVsID0gcm93bmFtZXMobXRjYXJzKSwgY29sb3IgPSAicmVkIikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZywgY29sb3IgPSBmY3lsKSkgKw0KICAjIEFkZCBwb2ludHMgbGF5ZXIgd2l0aCBzaGFwZSAyNCBhbmQgY29sb3IgeWVsbG93DQogIGdlb21fcG9pbnQoc2hhcGUgPSAyNCwgY29sb3IgPSAieWVsbG93IikNCmBgYA0KIyMgR29pbmcgYWxsIG91dA0KDQpOb3csIHdlIHdpbGwgZ3JhZHVhbGx5IGFkZCBtb3JlIGFlc3RoZXRpY3MgbGF5ZXJzIHRvIHRoZSBwbG90LiBXZSdyZSBzdGlsbCB3b3JraW5nIHdpdGggdGhlIG10Y2FycyBkYXRhc2V0LCBidXQgdGhpcyB0aW1lIHdlJ3JlIHVzaW5nIG1vcmUgZmVhdHVyZXMgb2YgdGhlIGNhcnMuIEVhY2ggb2YgdGhlIGNvbHVtbnMgaXMgZGVzY3JpYmVkIG9uIHRoZSBtdGNhcnMgaGVscCBwYWdlLg0KDQpOb3RpY2UgdGhhdCBhZGRpbmcgbW9yZSBhZXN0aGV0aWMgbWFwcGluZ3MgdG8gb3VyIHBsb3QgaXMgbm90IGFsd2F5cyBhIGdvb2QgaWRlYSEgV2UgbWF5IGp1c3QgaW5jcmVhc2UgY29tcGxleGl0eSBhbmQgZGVjcmVhc2UgcmVhZGFiaWxpdHkuDQpgYGB7cn0NCiMgMyBhZXN0aGV0aWNzOiBxc2VjIHZzLiBtcGcsIGNvbG9yZWQgYnkgZmN5bA0KZ2dwbG90KG10Y2FycywgYWVzKG1wZywgcXNlYywgY29sb3IgPSBmY3lsKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KYGBge3J9DQojIDQgYWVzdGhldGljczogYWRkIGEgbWFwcGluZyBvZiBzaGFwZSB0byBmYW0NCmdncGxvdChtdGNhcnMsIGFlcyhtcGcsIHFzZWMsIGNvbG9yID0gZmN5bCwgc2hhcGUgPSBmYW0pKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQpgYGB7cn0NCiMgNSBhZXN0aGV0aWNzOiBhZGQgYSBtYXBwaW5nIG9mIHNpemUgdG8gaHAgLyB3dA0KZ2dwbG90KG10Y2FycywgYWVzKG1wZywgcXNlYywgY29sb3IgPSBmY3lsLCBzaGFwZSA9IGZhbSwgc2l6ZSA9IGhwL3d0KSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KQmV0d2VlbiB0aGUgeCBhbmQgeSBkaW1lbnNpb25zLCB0aGUgY29sb3IsIHNoYXBlLCBhbmQgc2l6ZSBvZiB0aGUgcG9pbnRzLCB5b3VyIHBsb3QgZGlzcGxheXMgZml2ZSBkaW1lbnNpb25zIG9mIHRoZSBkYXRhc2V0IQ0KDQojIE1vZGlmeWluZyBhZXN0aGV0aWNzDQoNCiMjIFVwZGF0aW5nIGFlc3RoZXRpYyBsYWJlbHMNCg0KV2UnbGwgbW9kaWZ5IHNvbWUgYWVzdGhldGljcyB0byBtYWtlIGEgYmFyIHBsb3Qgb2YgdGhlIG51bWJlciBvZiBjeWxpbmRlcnMgZm9yIGNhcnMgd2l0aCBkaWZmZXJlbnQgdHlwZXMgb2YgdHJhbnNtaXNzaW9uLg0KDQpXZSdsbCBhbHNvIG1ha2UgdXNlIG9mIHNvbWUgZnVuY3Rpb25zIGZvciBpbXByb3ZpbmcgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIHBsb3QuDQoNCiAtIGxhYnMoKSB0byBzZXQgdGhlIHgtIGFuZCB5LWF4aXMgbGFiZWxzLiBJdCB0YWtlcyBzdHJpbmdzIGZvciBlYWNoIGFyZ3VtZW50Lg0KIC0gY2FsZV9jb2xvcl9tYW51YWwoKSBkZWZpbmVzIHByb3BlcnRpZXMgb2YgdGhlIGNvbG9yIHNjYWxlIChpLmUuIGF4aXMpLiBUaGUgZmlyc3QgYXJndW1lbnQgc2V0cyB0aGUgbGVnZW5kIHRpdGxlLiB2YWx1ZXMgaXMgYSBuYW1lZCB2ZWN0b3Igb2YgY29sb3JzIHRvIHVzZS4NCg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmN5bCwgZmlsbCA9IGZhbSkpICsNCiAgZ2VvbV9iYXIoKSArDQogICMgU2V0IHRoZSBheGlzIGxhYmVscw0KICBsYWJzKHggPSAiTnVtYmVyIG9mIEN5bGluZGVycyIsIHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KcGFsZXR0ZSA8LSBjKGF1dG9tYXRpYyA9ICIjMzc3RUI4IiwgbWFudWFsID0gIiNFNDFBMUMiKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyhmY3lsLCBmaWxsID0gZmFtKSkgKw0KICBnZW9tX2JhcigpICsNCiAgbGFicyh4ID0gIk51bWJlciBvZiBDeWxpbmRlcnMiLCB5ID0gIkNvdW50IikgKw0KICAjIFNldCB0aGUgZmlsbCBjb2xvciBzY2FsZQ0KICBzY2FsZV9maWxsX21hbnVhbCgiVHJhbnNtaXNzaW9uIiwgdmFsdWVzID0gcGFsZXR0ZSkNCmBgYA0KYGBge3J9DQojIFNldCB0aGUgcG9zaXRpb24NCmdncGxvdChtdGNhcnMsIGFlcyhmY3lsLCBmaWxsID0gZmFtKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgbGFicyh4ID0gIk51bWJlciBvZiBDeWxpbmRlcnMiLCB5ID0gIkNvdW50IikgKw0KICBzY2FsZV9maWxsX21hbnVhbCgiVHJhbnNtaXNzaW9uIiwgdmFsdWVzID0gcGFsZXR0ZSkNCmBgYA0KQ2hvb3NpbmcgdGhlIHJpZ2h0IHBvc2l0aW9uIGFyZ3VtZW50IGlzIGFuIGltcG9ydGFudCBwYXJ0IG9mIG1ha2luZyBhIGdvb2QgcGxvdC4NCg0KIyMgU2V0dGluZyBhIGR1bW15IGFlc3RoZXRpYw0KDQoNCldlIHNhdyBhbGwgdGhlIHZpc2libGUgYWVzdGhldGljcyBjYW4gc2VydmUgYXMgYXR0cmlidXRlcyBhbmQgYWVzdGhldGljcywgYnV0IHdlIGxlZnQgb3V0IHggYW5kIHkuIFRoYXQncyBiZWNhdXNlIGFsdGhvdWdoIHdlIGNhbiBtYWtlIHVuaXZhcmlhdGUgcGxvdHMgKHN1Y2ggYXMgaGlzdG9ncmFtcywgd2hpY2ggeW91J2xsIGdldCB0byBpbiB0aGUgbmV4dCBjaGFwdGVyKSwgYSB5LWF4aXMgd2lsbCBhbHdheXMgYmUgcHJvdmlkZWQsIGV2ZW4gaWYgeW91IGRpZG4ndCBhc2sgZm9yIGl0Lg0KDQpXZSBjYW4gbWFrZSB1bml2YXJpYXRlIHBsb3RzIGluIGdncGxvdDIsIGJ1dCB3ZSB3aWxsIG5lZWQgdG8gYWRkIGEgZmFrZSB5IGF4aXMgYnkgbWFwcGluZyB5IHRvIHplcm8uDQoNCldoZW4gdXNpbmcgc2V0dGluZyB5LWF4aXMgbGltaXRzLCB3ZSBjYW4gc3BlY2lmeSB0aGUgbGltaXRzIGFzIHNlcGFyYXRlIGFyZ3VtZW50cywgb3IgYXMgYSBzaW5nbGUgbnVtZXJpYyB2ZWN0b3IuIFRoYXQgaXMsIHlsaW0obG8sIGhpKSBvciB5bGltKGMobG8sIGhpKSkuDQpgYGB7cn0NCiMgUGxvdCAwIHZzLiBtcGcNCmdncGxvdChtdGNhcnMsIGFlcyhtcGcsIDApKSArDQogICMgQWRkIGppdHRlciANCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKQ0KDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKG1wZywgMCkpICsNCiAgZ2VvbV9qaXR0ZXIoKSArDQogICMgU2V0IHRoZSB5LWF4aXMgbGltaXRzDQogIHlsaW0oLTIsIDIpDQpgYGANClRoZSBiZXN0IHdheSB0byBtYWtlIHlvdXIgcGxvdCBkZXBlbmRzIG9uIGEgbG90IG9mIGRpZmZlcmVudCBmYWN0b3JzIGFuZCBzb21ldGltZXMgZ2dwbG90MiBtaWdodCBub3QgYmUgdGhlIGJlc3QgY2hvaWNlLg0KDQojIyBBZXN0aGV0aWNzIGJlc3QgcHJhY3RpY2VzDQoNCiMjIyBBcHByb3ByaWF0ZSBtYXBwaW5ncw0KSW5jb3JyZWN0IGFlc3RoZXRpYyBtYXBwaW5nIGNhdXNlcyBjb25mdXNpb24gb3IgbWlzbGVhZHMgdGhlIGF1ZGllbmNlLg0KDQpUeXBpY2FsbHksIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgaXMgbWFwcGVkIG9udG8gdGhlIHRoZSB5LWF4aXMgYW5kIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZSBpcyBtYXBwZWQgb250byB0aGUgeC1heGlzLg0KIVthZXN0aGV0aWNzXShpbWFnZXMvYWVzdGhldGljczAxLnBuZykNCg0KIyMjIEZvcm0gZm9sbG93cyBmdW5jdGlvbg0KDQoNCiMjIyBGdW5jdGlvbg0KUHJpbWFyeToNCg0KLSBBY2N1cmF0ZSBhbmQgZWZmaWNpZW50IHJlcHJlc2VudGF0aW9ucw0KDQpTZWNvbmRhcnk6DQoNCi0gVmlzdWFsbHkgYXBwZWFsaW5nLCBiZWF1dGlmdWwgcGxvdHMNCg0KIyMjIEd1aWRpbmcgcHJpbmNpcGxlcw0KDQpOZXZlcjoNCg0KLSBNaXNyZXByZXNlbnQgb3Igb2JzY3VyZSBkYXRhDQotIENvbmZ1c2Ugdmlld2VycyB3aXRoIGNvbXBsZXhpdHkNCg0KQWx3YXlzOg0KDQotIENvbnNpZGVyIHRoZSBhdWRpZW5jZSBhbmQgcHVycG9zZSBvZiBldmVyeSBwbG90DQoNCiMjIyBUaGUgYmVzdCBjaG9pY2VzIGZvciBhZXN0aGV0aWNzDQoNCi0gRWZmZmljaWVudA0KDQogIC0gUHJvdmlkZXMgYSBmYXN0ZXIgb3ZlcnZpZXcgdGhhbiBudW1lcmljIHN1bW1hcmllcw0KIA0KLSBBY2N1cmF0ZQ0KDQogIC0gTWluaW1pemVzIGluZm9ybWF0aW9uIGxvc3MNCg0KIyBHZW9tZXRyaWVzDQoNCiMjIFNjYXR0ZXIgUGxvdHM6IE92ZXJwbG90dGluZw0KDQojIyMgTGFyZ2UgZGF0YXNldHMNCg0KU2NhdHRlciBwbG90cyAodXNpbmcgZ2VvbV9wb2ludCgpKSBhcmUgaW50dWl0aXZlLCBlYXNpbHkgdW5kZXJzdG9vZCwgYW5kIHZlcnkgY29tbW9uLCBidXQgd2UgbXVzdCBhbHdheXMgY29uc2lkZXIgb3ZlcnBsb3R0aW5nLCBwYXJ0aWN1bGFybHkgaW4gdGhlIGZvbGxvd2luZyBmb3VyIHNpdHVhdGlvbnM6DQoNCjEuIExhcmdlIGRhdGFzZXRzDQoyLiBBbGlnbmVkIHZhbHVlcyBvbiBhIHNpbmdsZSBheGlzDQozLiBMb3ctcHJlY2lzaW9uIGRhdGENCjQuIEludGVnZXIgZGF0YQ0KDQpUeXBpY2FsbHksIGFscGhhIGJsZW5kaW5nIChpLmUuIGFkZGluZyB0cmFuc3BhcmVuY3kpIGlzIHJlY29tbWVuZGVkIHdoZW4gdXNpbmcgc29saWQgc2hhcGVzLiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIHVzZSBvcGFxdWUsIGhvbGxvdyBzaGFwZXMuDQoNClNtYWxsIHBvaW50cyBhcmUgc3VpdGFibGUgZm9yIGxhcmdlIGRhdGFzZXRzIHdpdGggcmVnaW9ucyBvZiBoaWdoIGRlbnNpdHkgKGxvdHMgb2Ygb3ZlcmxhcHBpbmcpLg0KYGBge3J9DQojIFBsb3QgcHJpY2UgdnMuIGNhcmF0LCBjb2xvcmVkIGJ5IGNsYXJpdHkNCnBsdF9wcmljZV92c19jYXJhdF9ieV9jbGFyaXR5IDwtIGdncGxvdChkaWFtb25kcywgYWVzKGNhcmF0LCBwcmljZSwgY29sb3IgPSBjbGFyaXR5KSkNCiMgQWRkIGEgcG9pbnQgbGF5ZXIgd2l0aCB0aW55IHBvaW50cw0KcGx0X3ByaWNlX3ZzX2NhcmF0X2J5X2NsYXJpdHkgKyBnZW9tX3BvaW50KGFscGhhID0gMC41LCBzaGFwZSA9ICIuIikNCmBgYA0KYGBge3J9DQojIFNldCB0cmFuc3BhcmVuY3kgdG8gMC41LCBzZXQgc2hhcGUgdG8gMTYNCnBsdF9wcmljZV92c19jYXJhdF9ieV9jbGFyaXR5ICsgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2hhcGUgPSAxNikNCmBgYA0KIyMjIEFsaWduZWQgdmFsdWVzDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IGFub3RoZXIgY2FzZSB3aGVyZSB3ZSBzaG91bGQgYmUgYXdhcmUgb2Ygb3ZlcnBsb3R0aW5nOiBBbGlnbmluZyB2YWx1ZXMgb24gYSBzaW5nbGUgYXhpcy4NCg0KVGhpcyBvY2N1cnMgd2hlbiBvbmUgYXhpcyBpcyBjb250aW51b3VzIGFuZCB0aGUgb3RoZXIgaXMgY2F0ZWdvcmljYWwsIHdoaWNoIGNhbiBiZSBvdmVyY29tZSB3aXRoIHNvbWUgZm9ybSBvZiBqaXR0ZXJpbmcuDQpgYGB7cn0NCiMgUGxvdCBiYXNlDQpwbHRfbXBnX3ZzX2ZjeWxfYnlfZmFtIDwtIGdncGxvdChtdGNhcnMsIGFlcyhmY3lsLCBtcGcsIGNvbG9yID0gZmFtKSkNCmBgYA0KYGBge3J9DQojIERlZmF1bHQgcG9pbnRzIGFyZSBzaG93biBmb3IgY29tcGFyaXNvbg0KcGx0X21wZ192c19mY3lsX2J5X2ZhbSArIGdlb21fcG9pbnQoKQ0KYGBgDQpgYGB7cn0NCiMgRGVmYXVsdCBwb2ludHMgYXJlIHNob3duIGZvciBjb21wYXJpc29uDQpwbHRfbXBnX3ZzX2ZjeWxfYnlfZmFtICsgZ2VvbV9wb2ludCgpDQpgYGANCmBgYHtyfQ0KIyBBbHRlciB0aGUgcG9pbnQgcG9zaXRpb25zIGJ5IGppdHRlcmluZywgd2lkdGggMC4zDQpwbHRfbXBnX3ZzX2ZjeWxfYnlfZmFtICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMykpDQpgYGANCmBgYHtyfQ0KIyBEZWZhdWx0IHBvaW50cyBhcmUgc2hvd24gZm9yIGNvbXBhcmlzb24NCnBsdF9tcGdfdnNfZmN5bF9ieV9mYW0gKyBnZW9tX3BvaW50KCkNCmBgYA0KYGBge3J9DQojIE5vdyBqaXR0ZXIgYW5kIGRvZGdlIHRoZSBwb2ludCBwb3NpdGlvbnMNCnBsdF9tcGdfdnNfZmN5bF9ieV9mYW0gKyBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2Uoaml0dGVyLndpZHRoID0gMC4zLCBkb2RnZS53aWR0aCA9IDAuMykpDQpgYGANCiMjIyBMb3ctcHJlY2lzaW9uIGRhdGENCg0KT3ZlcnBsb3R0aW5nIDM6IExvdy1wcmVjaXNpb24gZGF0YQ0KV2UgYWxyZWFkeSBzYXcgaG93IHRvIGRlYWwgd2l0aCBvdmVycGxvdHRpbmcgd2hlbiB1c2luZyBnZW9tX3BvaW50KCkgaW4gdHdvIGNhc2VzOg0KDQoxLglMYXJnZSBkYXRhc2V0cw0KMi4JQWxpZ25lZCB2YWx1ZXMgb24gYSBzaW5nbGUgYXhpcw0KDQpXZSB1c2VkIHBvc2l0aW9uID0gJ2ppdHRlcicgaW5zaWRlIGdlb21fcG9pbnQoKSBvciBnZW9tX2ppdHRlcigpLg0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBhbm90aGVyIGNhc2U6DQoNCjMuCUxvdy1wcmVjaXNpb24gZGF0YQ0KDQpUaGlzIHJlc3VsdHMgZnJvbSBsb3ctcmVzb2x1dGlvbiBtZWFzdXJlbWVudHMgbGlrZSBpbiB0aGUgaXJpcyBkYXRhc2V0LCB3aGljaCBpcyBtZWFzdXJlZCB0byAxbW0gcHJlY2lzaW9uLiBJdCdzIHNpbWlsYXIgdG8gY2FzZSAyLCBidXQgaW4gdGhpcyBjYXNlIHdlIGNhbiBqaXR0ZXIgb24gYm90aCB0aGUgeCBhbmQgeSBheGlzLg0KYGBge3J9DQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsNCiAgIyBTd2FwIGZvciBqaXR0ZXIgbGF5ZXIgd2l0aCB3aWR0aCAwLjENCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsd2lkdGggPSAwLjEpDQpgYGANCmBgYHtyfQ0KI2ppdHRlciB3aXRoaW4gZ2VvbV9wb2ludA0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKSArDQogICMgU2V0IHRoZSBwb3NpdGlvbiB0byBqaXR0ZXINCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgcG9zaXRpb24gPSAiaml0dGVyIikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsNCiAgIyBVc2UgYSBqaXR0ZXIgcG9zaXRpb24gZnVuY3Rpb24gd2l0aCB3aWR0aCAwLjENCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjEpKQ0KYGBgDQpOb3RpY2UgdGhhdCBqaXR0ZXIgY2FuIGJlIGEgZ2VvbSBpdHNlbGYgKGkuZS4gZ2VvbV9qaXR0ZXIoKSksIGFuIGFyZ3VtZW50IGluIGdlb21fcG9pbnQoKSAoaS5lLiBwb3NpdGlvbiA9ICJqaXR0ZXIiKSwgb3IgYSBwb3NpdGlvbiBmdW5jdGlvbiwgKGkuZS4gcG9zaXRpb25faml0dGVyKCkpLg0KIA0KIyMjIEludGVnZXIgZGF0YQ0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgbGFzdCBjYXNlIG9mIGRlYWxpbmcgd2l0aCBvdmVycGxvdHRpbmc6DQoNCjEuIEludGVnZXIgZGF0YQ0KDQpUaGlzIGNhbiBiZSB0eXBlIGludGVnZXIgKGkuZS4gMSAsMiwgMy4uLikgb3IgY2F0ZWdvcmljYWwgKGkuZS4gY2xhc3MgZmFjdG9yKSB2YXJpYWJsZXMuIGZhY3RvciBpcyBqdXN0IGEgc3BlY2lhbCBjbGFzcyBvZiB0eXBlIGludGVnZXIuDQoNCldlJ2xsIHR5cGljYWxseSBoYXZlIGEgc21hbGwsIGRlZmluZWQgbnVtYmVyIG9mIGludGVyc2VjdGlvbnMgYmV0d2VlbiB0d28gdmFyaWFibGVzLCB3aGljaCBpcyBzaW1pbGFyIHRvIGNhc2UgMywgYnV0IHlvdSBtYXkgbWlzcyBpdCBpZiB5b3UgZG9uJ3QgcmVhbGl6ZSB0aGF0IGludGVnZXIgYW5kIGZhY3RvciBkYXRhIGFyZSB0aGUgc2FtZSBhcyBsb3cgcHJlY2lzaW9uIGRhdGEuDQoNClRoZSBWb2NhYiBkYXRhc2V0IHByb3ZpZGVkIGNvbnRhaW5zIHRoZSB5ZWFycyBvZiBlZHVjYXRpb24gYW5kIHZvY2FidWxhcnkgdGVzdCBzY29yZXMgZnJvbSByZXNwb25kZW50cyB0byBVUyBHZW5lcmFsIFNvY2lhbCBTdXJ2ZXlzIGZyb20gMTk3Mi0yMDA0Lg0KYGBge3J9DQpsaWJyYXJ5KGNhckRhdGEpDQojIEV4YW1pbmUgdGhlIHN0cnVjdHVyZSBvZiBWb2NhYg0Kc3RyKFZvY2FiKQ0KDQojIFBsb3Qgdm9jYWJ1bGFyeSB2cy4gZWR1Y2F0aW9uDQpnZ3Bsb3QoVm9jYWIsIGFlcyhlZHVjYXRpb24sIHZvY2FidWxhcnkpKSArDQogICMgQWRkIGEgcG9pbnQgbGF5ZXINCiAgZ2VvbV9wb2ludCgpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFZvY2FiLCBhZXMoZWR1Y2F0aW9uLCB2b2NhYnVsYXJ5KSkgKw0KICAjIENoYW5nZSB0byBhIGppdHRlciBsYXllcg0KICBnZW9tX2ppdHRlcigpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFZvY2FiLCBhZXMoZWR1Y2F0aW9uLCB2b2NhYnVsYXJ5KSkgKw0KICAjIFNldCB0aGUgdHJhbnNwYXJlbmN5IHRvIDAuMg0KICBnZW9tX2ppdHRlcihhbHBoYT0wLjIpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFZvY2FiLCBhZXMoZWR1Y2F0aW9uLCB2b2NhYnVsYXJ5KSkgKw0KICAjIFNldCB0aGUgc2hhcGUgdG8gMQ0KICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2hhcGUgPSAxKQ0KYGBgDQpOb3RpY2UgaG93IGppdHRlcmluZyBhbmQgYWxwaGEgYmxlbmRpbmcgc2VydmVzIGFzIGEgZ3JlYXQgc29sdXRpb24gdG8gdGhlIG92ZXJwbG90dGluZyBwcm9ibGVtIGhlcmUuIFNldHRpbmcgdGhlIHNoYXBlIHRvIDEgZGlkbid0IHJlYWxseSBoZWxwLCBidXQgaXQgd2FzIHVzZWZ1bCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2VzIHdoZW4geW91IGhhZCBsZXNzIGRhdGEuIFdlIG5lZWQgdG8gY29uc2lkZXIgZWFjaCBwbG90IGluZGl2aWR1YWxseS4gDQoNCiMjIEhpc3RvZ3JhbXMNCg0KSGlzdG9ncmFtcyBjdXQgdXAgYSBjb250aW51b3VzIHZhcmlhYmxlIGludG8gZGlzY3JldGUgYmlucyBhbmQsIGJ5IGRlZmF1bHQsIG1hcHMgdGhlIGludGVybmFsbHkgY2FsY3VsYXRlZCBjb3VudCB2YXJpYWJsZSAodGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBiaW4pIG9udG8gdGhlIHkgYWVzdGhldGljLiBBbiBpbnRlcm5hbCB2YXJpYWJsZSBjYWxsZWQgZGVuc2l0eSBjYW4gYmUgYWNjZXNzZWQgYnkgdXNpbmcgdGhlIC4uIG5vdGF0aW9uLCBpLmUuIC4uZGVuc2l0eS4uLiBQbG90dGluZyB0aGlzIHZhcmlhYmxlIHdpbGwgc2hvdyB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5LCB3aGljaCBpcyB0aGUgaGVpZ2h0IHRpbWVzIHRoZSB3aWR0aCBvZiBlYWNoIGJpbi4NCmBgYHtyfQ0KIyBQbG90IG1wZw0KZ2dwbG90KG10Y2FycywgYWVzKG1wZykpICsNCiAgIyBBZGQgYSBoaXN0b2dyYW0gbGF5ZXINCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyhtcGcpKSArDQogICMgU2V0IHRoZSBiaW53aWR0aCB0byAxDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkNCmBgYA0KYGBge3J9DQojIE1hcCB5IHRvIC4uZGVuc2l0eS4uDQpnZ3Bsb3QobXRjYXJzLCBhZXMobXBnLCAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQ0KYGBgDQpgYGB7cn0NCmRhdGFjYW1wX2xpZ2h0X2JsdWUgPC0gIiM1MUE4QzkiDQoNCmdncGxvdChtdGNhcnMsIGFlcyhtcGcsIC4uZGVuc2l0eS4uKSkgKw0KICAjIFNldCB0aGUgZmlsbCBjb2xvciB0byBkYXRhY2FtcF9saWdodF9ibHVlDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgZmlsbCA9IGRhdGFjYW1wX2xpZ2h0X2JsdWUpDQpgYGANCkhpc3RvZ3JhbXMgYXJlIG9uZSBvZiB0aGUgbW9zdCBjb21tb24gZXhwbG9yYXRvcnkgcGxvdHMgZm9yIGNvbnRpbnVvdXMgZGF0YS4gSWYgeW91IHdhbnQgdG8gdXNlIGRlbnNpdHkgb24gdGhlIHktYXhpcyBiZSBzdXJlIHRvIHNldCB5b3VyIGJpbndpZHRoIHRvIGFuIGludHVpdGl2ZSB2YWx1ZS4NCg0KIyMjIFBvc2l0aW9ucyBpbiBoaXN0b2dyYW1zDQoNCkhlcmUsIHdlJ2xsIGV4YW1pbmUgdGhlIHZhcmlvdXMgd2F5cyBvZiBhcHBseWluZyBwb3NpdGlvbnMgdG8gaGlzdG9ncmFtcy4gZ2VvbV9oaXN0b2dyYW0oKSwgYSBzcGVjaWFsIGNhc2Ugb2YgZ2VvbV9iYXIoKSwgaGFzIGEgcG9zaXRpb24gYXJndW1lbnQgdGhhdCBjYW4gdGFrZSBvbiB0aGUgZm9sbG93aW5nIHZhbHVlczoNCg0KLSBzdGFjayAodGhlIGRlZmF1bHQpOiBCYXJzIGZvciBkaWZmZXJlbnQgZ3JvdXBzIGFyZSBzdGFja2VkIG9uIHRvcCBvZiBlYWNoIG90aGVyLg0KLSBkb2RnZTogQmFycyBmb3IgZGlmZmVyZW50IGdyb3VwcyBhcmUgcGxhY2VkIHNpZGUgYnkgc2lkZS4NCi0gZmlsbDogQmFycyBmb3IgZGlmZmVyZW50IGdyb3VwcyBhcmUgc2hvd24gYXMgcHJvcG9ydGlvbnMuDQotIGlkZW50aXR5OiBQbG90IHRoZSB2YWx1ZXMgYXMgdGhleSBhcHBlYXIgaW4gdGhlIGRhdGFzZXQuDQpgYGB7cn0NCiMgVXBkYXRlIHRoZSBhZXN0aGV0aWNzIHNvIHRoZSBmaWxsIGNvbG9yIGlzIGJ5IGZhbQ0KZ2dwbG90KG10Y2FycywgYWVzKG1wZywgZmlsbCA9IGZhbSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyhtcGcsIGZpbGwgPSBmYW0pKSArDQogICMgQ2hhbmdlIHRoZSBwb3NpdGlvbiB0byBkb2RnZQ0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIHBvc2l0aW9uID0gImRvZGdlIikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMobXBnLCBmaWxsID0gZmFtKSkgKw0KICAjIENoYW5nZSB0aGUgcG9zaXRpb24gdG8gZmlsbA0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIHBvc2l0aW9uID0gImZpbGwiKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyhtcGcsIGZpbGwgPSBmYW0pKSArDQogICMgQ2hhbmdlIHRoZSBwb3NpdGlvbiB0byBpZGVudGl0eSwgd2l0aCB0cmFuc3BhcmVuY3kgMC40DQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuNCkNCmBgYA0KIyMgQmFyIHBsb3RzDQoNCiMjIyBQb3NpdGlvbiBpbiBiYXIgYW5kIGNvbCBwbG90cw0KDQpMZXQncyBzZWUgaG93IHRoZSBwb3NpdGlvbiBhcmd1bWVudCBjaGFuZ2VzIGdlb21fYmFyKCkuDQoNCldlIGhhdmUgdGhyZWUgcG9zaXRpb24gb3B0aW9uczoNCg0KLSBzdGFjazogVGhlIGRlZmF1bHQNCi0gZG9kZ2U6IFByZWZlcnJlZA0KLSBmaWxsOiBUbyBzaG93IHByb3BvcnRpb25zDQoNCldoaWxlIHdlIHdpbGwgYmUgdXNpbmcgZ2VvbV9iYXIoKSBoZXJlLCBub3RlIHRoYXQgdGhlIGZ1bmN0aW9uIGdlb21fY29sKCkgaXMganVzdCBnZW9tX2JhcigpIHdoZXJlIGJvdGggdGhlIHBvc2l0aW9uIGFuZCBzdGF0IGFyZ3VtZW50cyBhcmUgc2V0IHRvICJpZGVudGl0eSIuIEl0IGlzIHVzZWQgd2hlbiB3ZSB3YW50IHRoZSBoZWlnaHRzIG9mIHRoZSBiYXJzIHRvIHJlcHJlc2VudCB0aGUgZXhhY3QgdmFsdWVzIGluIHRoZSBkYXRhLg0KYGBge3J9DQojIFBsb3QgZmN5bCwgZmlsbGVkIGJ5IGZhbQ0KZ2dwbG90KG10Y2FycywgYWVzKGZjeWwsIGZpbGwgPSBmYW0pKSArDQogICMgQWRkIGEgYmFyIGxheWVyDQogIGdlb21fYmFyKCkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmN5bCwgZmlsbCA9IGZhbSkpICsNCiAgIyBTZXQgdGhlIHBvc2l0aW9uIHRvICJmaWxsIg0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmN5bCwgZmlsbCA9IGZhbSkpICsNCiAgIyBDaGFuZ2UgdGhlIHBvc2l0aW9uIHRvICJkb2RnZSINCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQ0KYGBgDQpEaWZmZXJlbnQga2luZHMgb2YgcGxvdHMgbmVlZCBkaWZmZXJlbnQgcG9zaXRpb24gYXJndW1lbnRzLCBzbyBpdCdzIGltcG9ydGFudCB0byBiZSBmYW1pbGlhciB3aXRoIHRoaXMgYXR0cmlidXRlLg0KDQojIyMgT3ZlcmxhcHBpbmcgYmFyIHBsb3RzDQoNCldlIGNhbiBjdXN0b21pemUgYmFyIHBsb3RzIGZ1cnRoZXIgYnkgYWRqdXN0aW5nIHRoZSBkb2RnaW5nIHNvIHRoYXQgb3VyIGJhcnMgcGFydGlhbGx5IG92ZXJsYXAgZWFjaCBvdGhlci4gSW5zdGVhZCBvZiB1c2luZyBwb3NpdGlvbiA9ICJkb2RnZSIsIHdlJ3JlIGdvaW5nIHRvIHVzZSBwb3NpdGlvbl9kb2RnZSgpLCBsaWtlIHdlIGRpZCB3aXRoIHBvc2l0aW9uX2ppdHRlcigpIGluIHRoZSB0aGUgcHJldmlvdXNseS4gSGVyZSwgd2UnbGwgc2F2ZSB0aGlzIGFzIGFuIG9iamVjdCwgcG9zbl9kLCBzbyB0aGF0IHdlIGNhbiBlYXNpbHkgcmV1c2UgaXQuDQoNClJlbWVtYmVyLCB0aGUgcmVhc29uIHdlIHdhbnQgdG8gdXNlIHBvc2l0aW9uX2RvZGdlKCkgKGFuZCBwb3NpdGlvbl9qaXR0ZXIoKSkgaXMgdG8gc3BlY2lmeSBob3cgbXVjaCBkb2RnaW5nIChvciBqaXR0ZXJpbmcpIHlvdSB3YW50Lg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoY3lsLCBmaWxsID0gZmFtKSkgKw0KICAjIENoYW5nZSBwb3NpdGlvbiB0byB1c2UgdGhlIGZ1bmN0aW9uYWwgZm9ybSwgd2l0aCB3aWR0aCAwLjINCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuMikpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKGN5bCwgZmlsbCA9IGZhbSkpICsNCiAgIyBTZXQgdGhlIHRyYW5zcGFyZW5jeSB0byAwLjYNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuMiksIGFscGhhID0gMC42KQ0KYGBgDQpCeSB1c2luZyB0aGVzZSBwb3NpdGlvbiBmdW5jdGlvbnMsIHdlIGNhbiBjdXN0b21pemUgeW91ciBwbG90IHRvIHN1aXQgeW91ciBuZWVkcy4NCg0KIyMjIEJhciBwbG90czogc2VxdWVudGlhbCBjb2xvciBwYWxldHRlDQoNCldlJ2xsIGZpbGwgZWFjaCBzZWdtZW50IGFjY29yZGluZyB0byBhbiBvcmRpbmFsIHZhcmlhYmxlLiBUaGUgYmVzdCB3YXkgdG8gZG8gdGhhdCBpcyB3aXRoIGEgc2VxdWVudGlhbCBjb2xvciBwYWxldHRlLg0KYGBge3J9DQojZXhhbXBsZSBvZiB1c2luZyBhIHNlcXVlbnRpYWwgY29sb3IgcGFsZXR0ZQ0KZ2dwbG90KG10Y2FycywgYWVzKGZjeWwsIGZpbGwgPSBmYW0pKSArDQogIGdlb21fYmFyKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQ0KYGBgDQpgYGB7cn0NClZvY2FiID0gVm9jYWIgJT4lIA0KICBtdXRhdGUodm9jYWJ1bGFyeSA9IGFzLmZhY3Rvcih2b2NhYnVsYXJ5KSkNCiMgUGxvdCBlZHVjYXRpb24sIGZpbGxlZCBieSB2b2NhYnVsYXJ5DQpnZ3Bsb3QoVm9jYWIsIGFlcyhlZHVjYXRpb24sIGZpbGwgPSB2b2NhYnVsYXJ5KSkgKw0KICBnZW9tX2JhcigpDQpgYGANCg0KYGBge3J9DQojIFBsb3QgZWR1Y2F0aW9uLCBmaWxsZWQgYnkgdm9jYWJ1bGFyeQ0KZ2dwbG90KFZvY2FiLCBhZXMoZWR1Y2F0aW9uLCBmaWxsID0gdm9jYWJ1bGFyeSkpICsNCiAgIyBBZGQgYSBiYXIgbGF5ZXIgd2l0aCBwb3NpdGlvbiAiZmlsbCINCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpDQpgYGANCmBgYHtyfQ0KIyBQbG90IGVkdWNhdGlvbiwgZmlsbGVkIGJ5IHZvY2FidWxhcnkNCmdncGxvdChWb2NhYiwgYWVzKGVkdWNhdGlvbiwgZmlsbCA9IHZvY2FidWxhcnkpKSArDQogICMgQWRkIGEgYmFyIGxheWVyIHdpdGggcG9zaXRpb24gImZpbGwiDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogICMgQWRkIGEgYnJld2VyIGZpbGwgc2NhbGUgd2l0aCBkZWZhdWx0IHBhbGV0dGUNCiAgc2NhbGVfZmlsbF9icmV3ZXIoKQ0KYGBgDQojIyBMaW5lIHBsb3RzDQoNCldlJ2xsIHVzZSB0aGUgZWNvbm9taWNzIGRhdGFzZXQgdG8gbWFrZSBzb21lIGxpbmUgcGxvdHMuIFRoZSBkYXRhc2V0IGNvbnRhaW5zIGEgdGltZSBzZXJpZXMgZm9yIHVuZW1wbG95bWVudCBhbmQgcG9wdWxhdGlvbiBzdGF0aXN0aWNzIGZyb20gdGhlIEZlZGVyYWwgUmVzZXJ2ZSBCYW5rIG9mIFN0LiBMb3VpcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gVGhlIGRhdGEgaXMgY29udGFpbmVkIGluIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuDQoNClRvIGJlZ2luIHdpdGgsIHdlIGNhbiBsb29rIGF0IGhvdyB0aGUgbWVkaWFuIHVuZW1wbG95bWVudCB0aW1lIGFuZCB0aGUgdW5lbXBsb3ltZW50IHJhdGUgKHRoZSBudW1iZXIgb2YgdW5lbXBsb3llZCBwZW9wbGUgYXMgYSBwcm9wb3J0aW9uIG9mIHRoZSBwb3B1bGF0aW9uKSBjaGFuZ2Ugb3ZlciB0aW1lLg0KYGBge3J9DQojIFByaW50IHRoZSBoZWFkIG9mIGVjb25vbWljcw0KaGVhZChlY29ub21pY3MpDQoNCiMgVXNpbmcgZWNvbm9taWNzLCBwbG90IHVuZW1wbG95IHZzLiBkYXRlDQpnZ3Bsb3QoZWNvbm9taWNzLCBhZXMoeCA9IGRhdGUsIHkgPSB1bmVtcGxveSkpICsNCiAgIyBNYWtlIGl0IGEgbGluZSBwbG90DQogIGdlb21fbGluZSgpDQpgYGANCmBgYHtyfQ0KIyBDaGFuZ2UgdGhlIHktYXhpcyB0byB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgcG9wdWxhdGlvbiB0aGF0IGlzIHVuZW1wbG95ZWQNCmdncGxvdChlY29ub21pY3MsIGFlcyhkYXRlLCB1bmVtcGxveS9wb3ApKSArDQogIGdlb21fbGluZSgpDQpgYGANCiMjIyBNdWx0aXBsZSB0aW1lIHNlcmllcw0KV2UgYWxyZWFkeSBzYXcgaG93IHRoZSBmb3JtIG9mIHlvdXIgZGF0YSBhZmZlY3RzIGhvdyB5b3UgY2FuIHBsb3QgaXQuIExldCdzIGV4cGxvcmUgdGhhdCBmdXJ0aGVyIHdpdGggbXVsdGlwbGUgdGltZSBzZXJpZXMuIEhlcmUsIGl0J3MgaW1wb3J0YW50IHRoYXQgYWxsIGxpbmVzIGFyZSBvbiB0aGUgc2FtZSBzY2FsZSwgYW5kIGlmIHBvc3NpYmxlLCBvbiB0aGUgc2FtZSBwbG90Lg0KDQpmaXNoLnNwZWNpZXMgY29udGFpbnMgdGhlIGdsb2JhbCBjYXB0dXJlIHJhdGVzIG9mIHNldmVuIHNhbG1vbiBzcGVjaWVzIGZyb20gMTk1MOKAkzIwMTAuIEVhY2ggdmFyaWFibGUgKGNvbHVtbikgaXMgYSBTYWxtb24gc3BlY2llcyBhbmQgZWFjaCBvYnNlcnZhdGlvbiAocm93KSBpcyBvbmUgeWVhci4gZmlzaC50aWR5IGNvbnRhaW5zIHRoZSBzYW1lIGRhdGEsIGJ1dCBpbiB0aHJlZSBjb2x1bW5zOiBTcGVjaWVzLCBZZWFyLCBhbmQgQ2FwdHVyZSAoaS5lLiBvbmUgdmFyaWFibGUgcGVyIGNvbHVtbikuDQpgYGB7cn0NCmxvYWQoImZpc2guUkRhdGEiKQ0KYGBgDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpDQojIFVzZSBnYXRoZXIgdG8gZ28gZnJvbSBmaXNoLnNwZWNpZXMgdG8gZmlzaC50aWR5DQpmaXNoLnRpZHkgPC0gZ2F0aGVyKGZpc2guc3BlY2llcywgU3BlY2llcywgQ2FwdHVyZSwgLVllYXIpDQpgYGANCmBgYHtyfQ0Kc3RyKGZpc2guc3BlY2llcykNCnN0cihmaXNoLnRpZHkpDQpgYGANCmBgYHtyfQ0KIyBQbG90IHRoZSBSYWluYm93IFNhbG1vbiB0aW1lIHNlcmllcw0KZ2dwbG90KGZpc2guc3BlY2llcywgYWVzKHggPSBZZWFyLCB5ID0gUmFpbmJvdykpICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KYGBge3J9DQojIFBsb3QgdGhlIFBpbmsgU2FsbW9uIHRpbWUgc2VyaWVzDQpnZ3Bsb3QoZmlzaC5zcGVjaWVzLCBhZXMoeCA9IFllYXIsIHkgPSBQaW5rKSkgKw0KICBnZW9tX2xpbmUoKQ0KYGBgDQpgYGB7cn0NCiMgUGxvdCBtdWx0aXBsZSB0aW1lLXNlcmllcyBieSBncm91cGluZyBieSBzcGVjaWVzDQpnZ3Bsb3QoZmlzaC50aWR5LCBhZXMoWWVhciwgQ2FwdHVyZSkpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IFNwZWNpZXMpKQ0KYGBgDQpgYGB7cn0NCiMgUGxvdCBtdWx0aXBsZSB0aW1lLXNlcmllcyBieSBjb2xvcmluZyBieSBzcGVjaWVzDQpnZ3Bsb3QoZmlzaC50aWR5LCBhZXMoeCA9IFllYXIsIHkgPSBDYXB0dXJlLCBjb2xvciA9IFNwZWNpZXMpKSArDQogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBTcGVjaWVzKSkNCmBgYA0KQXMgd2UgY2FuIHNlZSBpbiB0aGUgdGhlIGxhc3QgY291cGxlIG9mIHBsb3RzLCBhIGdyb3VwaW5nIGFlc3RoZXRpYyB3YXMgdml0YWwgaGVyZS4gSWYgeW91IGRvbid0IHNwZWNpZnkgY29sb3IgPSBTcGVjaWVzLCB5b3UnbGwgZ2V0IGEgbWVzcyBvZiBsaW5lcy4NCg0KIyBUaGVtZXMNCg0KIyMjIE1vdmluZyB0aGUgbGVnZW5kDQoNClRvIGNoYW5nZSBzdHlsaXN0aWMgZWxlbWVudHMgb2YgYSBwbG90LCBjYWxsIHRoZW1lKCkgYW5kIHNldCBwbG90IHByb3BlcnRpZXMgdG8gYSBuZXcgdmFsdWUuIEZvciBleGFtcGxlLCB0aGUgZm9sbG93aW5nIGNoYW5nZXMgdGhlIGxlZ2VuZCBwb3NpdGlvbi4NCg0KICAgIHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBuZXdfdmFsdWUpDQoNCkhlcmUsIHRoZSBuZXcgdmFsdWUgY2FuIGJlDQoNCi0gInRvcCIsICJib3R0b20iLCAibGVmdCIsIG9yICJyaWdodCciOiBwbGFjZSBpdCBhdCB0aGF0IHNpZGUgb2YgdGhlIHBsb3QuDQotICJub25lIjogZG9uJ3QgZHJhdyBpdC4NCi0gYyh4LCB5KTogYygwLCAwKSBtZWFucyB0aGUgYm90dG9tLWxlZnQgYW5kIGMoMSwgMSkgbWVhbnMgdGhlIHRvcC1yaWdodC4NCmBgYHtyfQ0KcmVjZXNzIDwtIGRhdGEuZnJhbWUoDQogIGJlZ2luID0gYygiMTk2OS0xMi0wMSIsIjE5NzMtMTEtMDEiLCIxOTgwLTAxLTAxIiwiMTk4MS0wNy0wMSIsIjE5OTAtMDctMDEiLCIyMDAxLTAzLTAxIiwgIjIwMDctMTItMDEiKSwgDQogIGVuZCA9IGMoIjE5NzAtMTEtMDEiLCIxOTc1LTAzLTAxIiwiMTk4MC0wNy0wMSIsIjE5ODItMTEtMDEiLCIxOTkxLTAzLTAxIiwiMjAwMS0xMS0wMSIsICIyMDA5LTA3LTMwIiksDQogIGV2ZW50ID0gYygiRmlzY2FsICYgTW9uZXRhcnlcbnRpZ2h0ZW5pbmciLCAiMTk3MyBPaWwgY3Jpc2lzIiwgIkRvdWJsZSBkaXAgSSIsIkRvdWJsZSBkaXAgSUkiLCAiT2lsIHByaWNlIHNob2NrIiwgIkRvdC1jb20gYnViYmxlIiwgIlN1Yi1wcmltZVxubW9ydGdhZ2UgY3Jpc2lzIiksDQogIHkgPSAgYyguMDE0MTU5ODEsIDAuMDIwNjc0MDIsIDAuMDI5NTExOTAsICAwLjAzNDE5MjAxLCAgMC4wMjc2NzMzOSwgMC4wMjE1OTY2MiwwLjAyNTIwNzE1KSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEYNCiAgKQ0KDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCnJlY2VzcyRiZWdpbiA8LSB5bWQgKHJlY2VzcyRiZWdpbikNCnJlY2VzcyRlbmQgPC0geW1kIChyZWNlc3MkZW5kKQ0KYGBgDQpgYGB7cn0NCnBsdF9wcm9wX3VuZW1wbG95ZWRfb3Zlcl90aW1lID0gZ2dwbG90KGVjb25vbWljcywgYWVzKHggPSBkYXRlLCB5ID0gdW5lbXBsb3kvcG9wKSkgKw0KICBnZ3RpdGxlKGMoIlRoZSBwZXJjZW50YWdlIG9mIHVuZW1wbG95ZWQgQW1lcmljYW5zIFxuIGluY3JlYXNlcyBzaGFycGx5IGR1cmluZyByZWNlc3Npb25zIikpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3JlY3QoZGF0YSA9IHJlY2VzcywgDQogICAgICAgICAgICBhZXMoeG1pbiA9IGJlZ2luLCB4bWF4ID0gZW5kLCB5bWluID0gLUluZiwgeW1heCA9ICtJbmYsIGZpbGwgPSAiUmVjZXNzaW9uIiksIA0KICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwgYWxwaGEgPSAwLjIpICsNCiAgZ2VvbV9sYWJlbChkYXRhID0gcmVjZXNzLCBhZXMoeCA9IGVuZCwgeSA9IHksIGxhYmVsPWV2ZW50KSwgc2l6ZSA9IDMpICsgDQogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICIiLCB2YWx1ZXM9InJlZCIsIGxhYmVsPSJSZWNlc3Npb25zIikNCg0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUNCmBgYA0KDQpgYGB7cn0NCiMgVmlldyB0aGUgZGVmYXVsdCBwbG90DQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZQ0KDQojIFJlbW92ZSBsZWdlbmQgZW50aXJlbHkNCnBsdF9wcm9wX3VuZW1wbG95ZWRfb3Zlcl90aW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQpgYGB7cn0NCiMgUG9zaXRpb24gdGhlIGxlZ2VuZCBhdCB0aGUgYm90dG9tIG9mIHRoZSBwbG90DQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQpgYGB7cn0NCiMgUG9zaXRpb24gdGhlIGxlZ2VuZCBpbnNpZGUgdGhlIHBsb3QgYXQgKDAuNiwgMC4xKQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuNiwwLjEpKQ0KYGBgDQpCdXQgYmUgY2FyZWZ1bCB3aGVuIHBsYWNpbmcgYSBsZWdlbmQgaW5zaWRlIHlvdXIgcGxvdHRpbmcgc3BhY2UuIFlvdSBjb3VsZCBlbmQgdXAgb2JzY3VyaW5nIGRhdGEuDQoNCiMjIyBNb2RpZnlpbmcgdGhlbWUgZWxlbWVudHMNCg0KTWFueSBwbG90IGVsZW1lbnRzIGhhdmUgbXVsdGlwbGUgcHJvcGVydGllcyB0aGF0IGNhbiBiZSBzZXQuIEZvciBleGFtcGxlLCBsaW5lIGVsZW1lbnRzIGluIHRoZSBwbG90IHN1Y2ggYXMgYXhlcyBhbmQgZ3JpZGxpbmVzIGhhdmUgYSBjb2xvciwgYSB0aGlja25lc3MgKHNpemUpLCBhbmQgYSBsaW5lIHR5cGUgKHNvbGlkIGxpbmUsIGRhc2hlZCwgb3IgZG90dGVkKS4gVG8gc2V0IHRoZSBzdHlsZSBvZiBhIGxpbmUsIHlvdSB1c2UgZWxlbWVudF9saW5lKCkuIEZvciBleGFtcGxlLCB0byBtYWtlIHRoZSBheGlzIGxpbmVzIGludG8gcmVkLCBkYXNoZWQgbGluZXMsIHlvdSB3b3VsZCB1c2UgdGhlIGZvbGxvd2luZy4NCg0KICAgIHAgKyB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikpDQogICAgDQpTaW1pbGFybHksIGVsZW1lbnRfcmVjdCgpIGNoYW5nZXMgcmVjdGFuZ2xlcyBhbmQgZWxlbWVudF90ZXh0KCkgY2hhbmdlcyB0ZXh0LiBZb3UgY2FuIHJlbW92ZSBhIHBsb3QgZWxlbWVudCB1c2luZyBlbGVtZW50X2JsYW5rKCkuDQpgYGB7cn0NCnBsdF9wcm9wX3VuZW1wbG95ZWRfb3Zlcl90aW1lICsNCiAgdGhlbWUoDQogICAgIyBGb3IgYWxsIHJlY3RhbmdsZXMsIHNldCB0aGUgZmlsbCBjb2xvciB0byBncmV5OTINCiAgICByZWN0ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTkyIiksDQogICAgIyBGb3IgdGhlIGxlZ2VuZCBrZXksIHR1cm4gb2ZmIHRoZSBvdXRsaW5lDQogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChjb2xvciA9IE5BKQ0KICApDQpgYGANCmBgYHtyfQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICB0aGVtZSgNCiAgICByZWN0ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTkyIiksDQogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChjb2xvciA9IE5BKSwNCiAgICAjIFR1cm4gb2ZmIGF4aXMgdGlja3MNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgICMgVHVybiBvZmYgdGhlIHBhbmVsIGdyaWQNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpDQogICkNCmBgYA0KYGBge3J9DQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZSArDQogIHRoZW1lKA0KICAgIHJlY3QgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmV5OTIiKSwNCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGNvbG9yID0gTkEpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjIEFkZCBtYWpvciB5LWF4aXMgcGFuZWwgZ3JpZCBsaW5lcyBiYWNrDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKA0KICAgICAgIyBTZXQgdGhlIGNvbG9yIHRvIHdoaXRlDQogICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAjIFNldCB0aGUgc2l6ZSB0byAwLjUNCiAgICAgIHNpemUgPSAwLjUsDQogICAgICAjIFNldCB0aGUgbGluZSB0eXBlIHRvIGRvdHRlZA0KICAgICAgbGluZXR5cGUgPSAiZG90dGVkIg0KICAgICkNCiAgKQ0KYGBgDQpgYGB7cn0NCnBsdF9wcm9wX3VuZW1wbG95ZWRfb3Zlcl90aW1lICsNCiAgdGhlbWUoDQogICAgcmVjdCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk5MiIpLA0KICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3IgPSBOQSksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZSgNCiAgICAgIGNvbG9yID0gIndoaXRlIiwNCiAgICAgIHNpemUgPSAwLjUsDQogICAgICBsaW5ldHlwZSA9ICJkb3R0ZWQiDQogICAgKSwNCiAgICAjIFNldCB0aGUgYXhpcyB0ZXh0IGNvbG9yIHRvIGdyZXkyNQ0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ImdyZXkyNSIpLA0KICAgICMgU2V0IHRoZSBwbG90IHRpdGxlIGZvbnQgZmFjZSB0byBpdGFsaWMgYW5kIGZvbnQgc2l6ZSB0byAxNg0KICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiaXRhbGljIikNCiAgKQ0KYGBgDQpFeGNlbGxlbnQgRXhwbGFuYXRvcnkgUGxvdCEgVGhpcyBwbG90IGlzIHJlYWR5IGZvciBwcmltZSB0aW1lIOKAkyBpdCdzIHByZXR0eSBBTkQgaW5mb3JtYXRpdmUuIE1ha2Ugc3VyZSB0aGF0IGFsbCB5b3VyIHRleHQgaXMgbGVnaWJsZSBmb3IgdGhlIGNvbnRleHQgaW4gd2hpY2ggaXQgd2lsbCBiZSB2aWV3ZWQuDQoNCiMjIyBNb2RpZnlpbmcgd2hpdGVzcGFjZQ0KDQpXaGl0ZXNwYWNlIG1lYW5zIGFsbCB0aGUgbm9uLXZpc2libGUgbWFyZ2lucyBhbmQgc3BhY2luZyBpbiB0aGUgcGxvdC4NCg0KVG8gc2V0IGEgc2luZ2xlIHdoaXRlc3BhY2UgdmFsdWUsIHVzZSB1bml0KHgsIHVuaXQpLCB3aGVyZSB4IGlzIHRoZSBhbW91bnQgYW5kIHVuaXQgaXMgdGhlIHVuaXQgb2YgbWVhc3VyZS4NCg0KQm9yZGVycyByZXF1aXJlIHlvdSB0byBzZXQgNCBwb3NpdGlvbnMsIHNvIHVzZSBtYXJnaW4odG9wLCByaWdodCwgYm90dG9tLCBsZWZ0LCB1bml0KS4gVG8gcmVtZW1iZXIgdGhlIG1hcmdpbiBvcmRlciwgdGhpbmsgVFJvdUJMZS4NCg0KVGhlIGRlZmF1bHQgdW5pdCBpcyAicHQiIChwb2ludHMpLCB3aGljaCBzY2FsZXMgd2VsbCB3aXRoIHRleHQuIE90aGVyIG9wdGlvbnMgaW5jbHVkZSAiY20iLCAiaW4iIChpbmNoZXMpIGFuZCAibGluZXMiIChvZiB0ZXh0KS4NCmBgYHtyfQ0KIyBWaWV3IHRoZSBvcmlnaW5hbCBwbG90DQpwbHRfbXBnX3ZzX3d0X2J5X2N5bCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZywgY29sb3IgPSBmY3lsKSkgKw0KICB5bGFiKCJNaWVscyBwZXIgZ2FsbG9uIikgKyANCiAgeGxhYigid2VpZ2h0ICgxMDAwL2xicykiKSArDQogIGdlb21fcG9pbnQoKQ0KcGx0X21wZ192c193dF9ieV9jeWwNCmBgYA0KYGBge3J9DQpwbHRfbXBnX3ZzX3d0X2J5X2N5bCArDQogIHRoZW1lKA0KICAgICMgU2V0IHRoZSBheGlzIHRpY2sgbGVuZ3RoIHRvIDIgbGluZXMNCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMiwgImxpbmVzIikNCiAgKQ0KYGBgDQpgYGB7cn0NCnBsdF9tcGdfdnNfd3RfYnlfY3lsICsNCiAgdGhlbWUoDQogICAgIyBTZXQgdGhlIGxlZ2VuZCBrZXkgc2l6ZSB0byAzIGNlbnRpbWV0ZXJzDQogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgzLCAiY20iKQ0KICApDQpgYGANCmBgYHtyfQ0KcGx0X21wZ192c193dF9ieV9jeWwgKw0KICB0aGVtZSgNCiAgICAjIFNldCB0aGUgbGVnZW5kIG1hcmdpbiB0byAoMjAsIDMwLCA0MCwgNTApIHBvaW50cw0KICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oMjAsIDMwLCA0MCwgNTAsICJwdCIpDQogICkNCmBgYA0KYGBge3J9DQpwbHRfbXBnX3ZzX3d0X2J5X2N5bCArDQogIHRoZW1lKA0KICAgICMgU2V0IHRoZSBwbG90IG1hcmdpbiB0byAoMTAsIDMwLCA1MCwgNzApIG1pbGxpbWV0ZXJzDQogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDMwLCA1MCwgNzAsICJtbSIpDQogICkNCmBgYA0KQ2hhbmdpbmcgdGhlIHdoaXRlc3BhY2UgY2FuIGJlIHVzZWZ1bCBpZiB5b3UgbmVlZCB0byBtYWtlIHlvdXIgcGxvdCBtb3JlIGNvbXBhY3QsIG9yIGlmIHlvdSB3YW50IHRvIGNyZWF0ZSBtb3JlIHNwYWNlIHRvIHJlZHVjZSDigJxidXNpbmVzc+KAnS4NCg0KIyMgQnVpbHQtaW4gVGhlbWVzDQoNCkJ1aWx0LWluIHRoZW1lcw0KSW4gYWRkaXRpb24gdG8gbWFraW5nIHlvdXIgb3duIHRoZW1lcywgdGhlcmUgYXJlIHNldmVyYWwgW291dC1vZi10aGUtYm94IHNvbHV0aW9uc10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dndGhlbWUuaHRtbCkgdGhhdCBtYXkgc2F2ZSB5b3UgbG90cyBvZiB0aW1lLg0KDQotIHRoZW1lX2dyYXkoKSBpcyB0aGUgZGVmYXVsdC4NCi0gdGhlbWVfYncoKSBpcyB1c2VmdWwgd2hlbiB5b3UgdXNlIHRyYW5zcGFyZW5jeS4NCi0gdGhlbWVfY2xhc3NpYygpIGlzIG1vcmUgdHJhZGl0aW9uYWwuDQotIHRoZW1lX3ZvaWQoKSByZW1vdmVzIGV2ZXJ5dGhpbmcgYnV0IHRoZSBkYXRhLg0KYGBge3J9DQojIEFkZCBhIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICB0aGVtZV9idygpDQpgYGANCmBgYHtyfQ0KIyBBZGQgYSBjbGFzc2ljIHRoZW1lDQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQpgYGB7cn0NCiMgQWRkIGEgdm9pZCB0aGVtZQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KVGhlIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZSB3b3JrcyByZWFsbHkgd2VsbCBpZiB5b3UgdXNlIHRyYW5zcGFyZW5jeSBpbiB5b3VyIHBsb3QuDQoNCiMjIEV4cGxvcmluZyBnZ3RoZW1lcyBwYWNrYWdlDQoNCk91dHNpZGUgb2YgZ2dwbG90MiwgYW5vdGhlciBzb3VyY2Ugb2YgYnVpbHQtaW4gdGhlbWVzIGlzIHRoZSBnZ3RoZW1lcyBwYWNrYWdlLiANCmBgYHtyfQ0KbGlicmFyeShnZ3RoZW1lcykNCmBgYA0KYGBge3J9DQojIFVzZSB0aGUgZml2ZXRoaXJ0eWVpZ2h0IHRoZW1lDQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZSArDQogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpDQpgYGANCmBgYHtyfQ0KIyBVc2UgVHVmdGUncyB0aGVtZQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICB0aGVtZV90dWZ0ZSgpDQpgYGANCmBgYHtyfQ0KIyBVc2UgdGhlIFdhbGwgU3RyZWV0IEpvdXJuYWwgdGhlbWUNCnBsdF9wcm9wX3VuZW1wbG95ZWRfb3Zlcl90aW1lICsNCiAgdGhlbWVfd3NqKCkNCmBgYA0KZ2d0aGVtZXMgaGFzIG92ZXIgMjAgdGhlbWVzIGZvciB5b3UgdG8gdHJ5Lg0KDQojIyBTZXR0aW5nIHRoZW1lcw0KDQpSZXVzaW5nIGEgdGhlbWUgYWNyb3NzIG1hbnkgcGxvdHMgaGVscHMgdG8gcHJvdmlkZSBhIGNvbnNpc3RlbnQgc3R5bGUuIFlvdSBoYXZlIHNldmVyYWwgb3B0aW9ucyBmb3IgdGhpcy4NCg0KMS4gQXNzaWduIHRoZSB0aGVtZSB0byBhIHZhcmlhYmxlLCBhbmQgYWRkIGl0IHRvIGVhY2ggcGxvdC4NCjIuIFNldCB5b3VyIHRoZW1lIGFzIHRoZSBkZWZhdWx0IHVzaW5nIHRoZW1lX3NldCgpLg0KDQpBIGdvb2Qgc3RyYXRlZ3kgdGhhdCB3ZSdsbCB1c2UgaGVyZSBpcyB0byBiZWdpbiB3aXRoIGEgYnVpbHQtaW4gdGhlbWUgdGhlbiBtb2RpZnkgaXQuDQpgYGB7cn0NCiMgU2F2ZSB0aGUgdGhlbWUgYXMgdGhlbWVfcmVjZXNzaW9uDQp0aGVtZV9yZWNlc3Npb24gPC0gdGhlbWUoDQogIHJlY3QgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmV5OTIiKSwNCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChjb2xvciA9IE5BKSwNCiAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuNSwgbGluZXR5cGUgPSAiZG90dGVkIiksDQogIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmV5MjUiKSwNCiAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxNiksDQogIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC42LCAwLjEpDQopDQoNCiMgQ29tYmluZSB0aGUgVHVmdGUgdGhlbWUgd2l0aCB0aGVtZV9yZWNlc3Npb24NCnRoZW1lX3R1ZnRlX3JlY2Vzc2lvbiA8LSB0aGVtZV90dWZ0ZSgpICsgdGhlbWVfcmVjZXNzaW9uDQoNCiMgQWRkIHRoZSBUdWZ0ZSByZWNlc3Npb24gdGhlbWUgdG8gdGhlIHBsb3QNCnBsdF9wcm9wX3VuZW1wbG95ZWRfb3Zlcl90aW1lICsgdGhlbWVfdHVmdGVfcmVjZXNzaW9uDQpgYGANCmBgYHtyfQ0KdGhlbWVfcmVjZXNzaW9uIDwtIHRoZW1lKA0KICByZWN0ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTkyIiksDQogIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3IgPSBOQSksDQogIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjUsIGxpbmV0eXBlID0gImRvdHRlZCIpLA0KICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1IiksDQogIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTYpLA0KICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNiwgMC4xKQ0KKQ0KdGhlbWVfdHVmdGVfcmVjZXNzaW9uIDwtIHRoZW1lX3R1ZnRlKCkgKyB0aGVtZV9yZWNlc3Npb24NCg0KIyBTZXQgdGhlbWVfdHVmdGVfcmVjZXNzaW9uIGFzIHRoZSBkZWZhdWx0IHRoZW1lDQp0aGVtZV9zZXQodGhlbWVfdHVmdGVfcmVjZXNzaW9uKSANCg0KIyBEcmF3IHRoZSBwbG90ICh3aXRob3V0IGV4cGxpY2l0bHkgYWRkaW5nIGEgdGhlbWUpDQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZQ0KYGBgDQojIyBQdWJsaWNhdGlvbi1xdWFsaXR5IHBsb3RzDQoNCmBgYHtyfQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICAjIEFkZCBUdWZ0ZSdzIHRoZW1lDQogIHRoZW1lX3R1ZnRlKCkNCmBgYA0KYGBge3J9DQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZSArDQogIHRoZW1lX3R1ZnRlKCkgKw0KICAjIEFkZCBpbmRpdmlkdWFsIHRoZW1lIGVsZW1lbnRzDQogIHRoZW1lKA0KICAgICMgVHVybiBvZmYgdGhlIGxlZ2VuZA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAjIFR1cm4gb2ZmIHRoZSBheGlzIHRpY2tzDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQpgYGANCmBgYHtyfQ0KcGx0X3Byb3BfdW5lbXBsb3llZF9vdmVyX3RpbWUgKw0KICB0aGVtZV90dWZ0ZSgpICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgIyBTZXQgdGhlIGF4aXMgdGl0bGUncyB0ZXh0IGNvbG9yIHRvIGdyZXk2MA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTYwIiksDQogICAgIyBTZXQgdGhlIGF4aXMgdGV4dCdzIHRleHQgY29sb3IgdG8gZ3JleTYwDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXk2MCIpDQogICkNCmBgYA0KYGBge3J9DQpwbHRfcHJvcF91bmVtcGxveWVkX292ZXJfdGltZSArDQogIHRoZW1lX3R1ZnRlKCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXk2MCIpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmV5NjAiKSwNCiAgICAjIFNldCB0aGUgcGFuZWwgZ3JpZGxpbmVzIG1ham9yIHkgdmFsdWVzDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKA0KICAgICAgIyBTZXQgdGhlIGNvbG9yIHRvIGdyZXk2MA0KICAgICAgY29sb3IgPSAiZ3JleTYwIiwNCiAgICAgICMgU2V0IHRoZSBzaXplIHRvIDAuMjUNCiAgICAgIHNpemUgPSAwLjI1LA0KICAgICAgIyBTZXQgdGhlIGxpbmV0eXBlIHRvIGRvdHRlZA0KICAgICAgbGluZXR5cGUgPSAiZG90dGVkIg0KICAgICkNCiAgKQ0KYGBgDQojIyBVc2luZyBnZW9tcyBmb3IgZXhwbGFuYXRvcnkgcGxvdHMNCg0KTGV0J3MgZm9jdXMgb24gcHJvZHVjaW5nIGJlYXV0aWZ1bCBhbmQgZWZmZWN0aXZlIGV4cGxhbmF0b3J5IHBsb3RzLiBJbiB0aGUgbmV4dCBjb3VwbGUgb2YgZXhlcmNpc2VzLCB3ZSdsbCBjcmVhdGUgYSBwbG90IHRoYXQgaXMgc2ltaWxhciB0byB0aGUgb25lIHNob3duIGluIHRoZSB2aWRlbyB1c2luZyBnbTIwMDcsIGEgZmlsdGVyZWQgc3Vic2V0IG9mIHRoZSBnYXBtaW5kZXIgZGF0YXNldC4NCg0KVGhpcyB0eXBlIG9mIHBsb3Qgd2lsbCBiZSBpbiBhbiBpbmZvLXZpeiBzdHlsZSwgbWVhbmluZyB0aGF0IGl0IHdvdWxkIGJlIHNpbWlsYXIgdG8gc29tZXRoaW5nIHlvdSdkIHNlZSBpbiBhIG1hZ2F6aW5lIG9yIHdlYnNpdGUgZm9yIGEgbW9zdGx5IGxheSBhdWRpZW5jZS4NCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdhcG1pbmRlcikNCmdhcG1pbmRlcg0KYGBgDQoNCg0KYGBge3J9DQpnbTIwMDcgPC0gZ2FwbWluZGVyICU+JSANCiAgZmlsdGVyKHllYXIgPT0gMjAwNykgJT4lIA0KICBzZWxlY3QoY291bnRyeSwgbGlmZUV4cCwgY29udGluZW50KSAlPiUgDQpmaWx0ZXIobGlmZUV4cCA+IDgwLjYgfCBsaWZlRXhwIDw0NikgJT4lIA0KICBhcnJhbmdlKGxpZmVFeHApDQpnbTIwMDcNCmdtMjAwN19mdWxsIDwtIGdhcG1pbmRlciAlPiUgDQogIGZpbHRlcih5ZWFyID09IDIwMDcpICU+JSANCiAgc2VsZWN0KGNvdW50cnksIGxpZmVFeHAsIGNvbnRpbmVudCkNCmBgYA0KYGBge3J9DQojIEFkZCBhIGdlb21fc2VnbWVudCgpIGxheWVyDQpnZ3Bsb3QoZ20yMDA3LCBhZXMoeCA9IGxpZmVFeHAsIHkgPSBjb3VudHJ5LCBjb2xvciA9IGxpZmVFeHApKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsNCiAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gMzAsIHllbmQgPSBjb3VudHJ5KSwgc2l6ZSA9IDIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpDQpgYGANCmBgYHtyfQ0KIyBBZGQgYSBnZW9tX3RleHQoKSBsYXllcg0KZ2dwbG90KGdtMjAwNywgYWVzKHggPSBsaWZlRXhwLCB5ID0gY291bnRyeSwgY29sb3IgPSBsaWZlRXhwKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA0KSArDQogIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IDMwLCB5ZW5kID0gY291bnRyeSksIHNpemUgPSAyKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBsaWZlRXhwKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMS41KSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKQ0KYGBgDQpgYGB7cn0NCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KIyBTZXQgdGhlIGNvbG9yIHNjYWxlDQpwYWxldHRlIDwtIGJyZXdlci5wYWwoNSwgIlJkWWxCdSIpWy0oMjo0KV0NCg0KIyBNb2RpZnkgdGhlIHNjYWxlcw0KZ2dwbG90KGdtMjAwNywgYWVzKHggPSBsaWZlRXhwLCB5ID0gY291bnRyeSwgY29sb3IgPSBsaWZlRXhwKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSA0KSArDQogIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IDMwLCB5ZW5kID0gY291bnRyeSksIHNpemUgPSAyKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChsaWZlRXhwLDEpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMS41KSArDQogIHNjYWxlX3hfY29udGludW91cygiIiwgZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygzMCw5MCksIHBvc2l0aW9uID0gInRvcCIpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHBhbGV0dGUpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpDQpgYGANCmBgYHtyfQ0KIyBTZXQgdGhlIGNvbG9yIHNjYWxlDQpwYWxldHRlIDwtIGJyZXdlci5wYWwoNSwgIlJkWWxCdSIpWy0oMjo0KV0NCg0KIyBBZGQgYSB0aXRsZSBhbmQgY2FwdGlvbg0KcGx0X2NvdW50cnlfdnNfbGlmZUV4cCA8LSBnZ3Bsb3QoZ20yMDA3LCBhZXMoeCA9IGxpZmVFeHAsIHkgPSBjb3VudHJ5LCBjb2xvciA9IGxpZmVFeHApKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsNCiAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gMzAsIHllbmQgPSBjb3VudHJ5KSwgc2l6ZSA9IDIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGxpZmVFeHAsMSkpLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAxLjUpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKCIiLCBleHBhbmQgPSBjKDAsMCksIGxpbWl0cyA9IGMoMzAsOTApLCBwb3NpdGlvbiA9ICJ0b3AiKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBwYWxldHRlKSArDQogIGxhYnModGl0bGUgPSAiSGlnaGVzdCBhbmQgbG93ZXN0IGxpZmUgZXhwZWN0YW5jaWVzLCAyMDA3IiwgY2FwdGlvbiA9ICJTb3VyY2U6IGdhcG1pbmRlciIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpDQpwbHRfY291bnRyeV92c19saWZlRXhwDQpgYGANCiMjIFVzaW5nIGFubm90YXRlKCkgZm9yIGVtYmVsbGlzaG1lbnRzDQoNCldlIGNvbXBsZXRlZCBvdXIgYmFzaWMgcGxvdC4gTm93IGxldCdzIHBvbGlzaCBpdCBieSBwbGF5aW5nIHdpdGggdGhlIHRoZW1lIGFuZCBhZGRpbmcgYW5ub3RhdGlvbnMuIEluIHRoaXMgZXhlcmNpc2UsIHdlJ2xsIHVzZSBhbm5vdGF0ZSgpIHRvIGFkZCB0ZXh0IGFuZCBhIGN1cnZlIHRvIHRoZSBwbG90Lg0KDQpUaGUgZm9sbG93aW5nIHZhbHVlcyBoYXZlIGJlZW4gY2FsY3VsYXRlZCBmb3IgeW91IHRvIGFzc2lzdCB3aXRoIGFkZGluZyBlbWJlbGxpc2htZW50cyB0byB0aGUgcGxvdDoNCmBgYHtyfQ0KZ2xvYmFsX21lYW4gPC0gbWVhbihnbTIwMDdfZnVsbCRsaWZlRXhwKQ0KeF9zdGFydCA8LSBnbG9iYWxfbWVhbiArIDQNCnlfc3RhcnQgPC0gNS41DQp4X2VuZCA8LSBnbG9iYWxfbWVhbg0KeV9lbmQgPC0gNy41DQpgYGANCg0KYGBge3J9DQojIERlZmluZSB0aGUgdGhlbWUNCnBsdF9jb3VudHJ5X3ZzX2xpZmVFeHAgPC0gcGx0X2NvdW50cnlfdnNfbGlmZUV4cCArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpLA0KICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpwbHRfY291bnRyeV92c19saWZlRXhwDQpgYGANCmBgYHtyfQ0KIyBBZGQgYSB2ZXJ0aWNhbCBsaW5lDQpwbHRfY291bnRyeV92c19saWZlRXhwIDwtIHBsdF9jb3VudHJ5X3ZzX2xpZmVFeHAgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBnbG9iYWxfbWVhbiwgY29sb3IgPSAiZ3JleTQwIiwgbGluZXR5cGUgPSAzKQ0KcGx0X2NvdW50cnlfdnNfbGlmZUV4cA0KYGBgDQpgYGB7cn0NCnBsdF9jb3VudHJ5X3ZzX2xpZmVFeHAgPC0gcGx0X2NvdW50cnlfdnNfbGlmZUV4cCAgKw0KICBhbm5vdGF0ZSgNCiAgICAidGV4dCIsDQogICAgeCA9IHhfc3RhcnQsIHkgPSB5X3N0YXJ0LA0KICAgIGxhYmVsID0gIlRoZVxuZ2xvYmFsXG5hdmVyYWdlIiwNCiAgICB2anVzdCA9IDEsIHNpemUgPSAzLCBjb2xvciA9ICJncmV5NDAiDQogICkNCnBsdF9jb3VudHJ5X3ZzX2xpZmVFeHANCmBgYA0KYGBge3J9DQpwbHRfY291bnRyeV92c19saWZlRXhwIDwtIHBsdF9jb3VudHJ5X3ZzX2xpZmVFeHAgICsNCiAgYW5ub3RhdGUoDQogICAgImN1cnZlIiwNCiAgICB4ID0geF9zdGFydCwgeSA9IHlfc3RhcnQsDQogICAgeGVuZCA9IHhfZW5kLCB5ZW5kID0geV9lbmQsDQogICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIiksIHR5cGUgPSAiY2xvc2VkIiksDQogICAgY29sb3IgPSAiZ3JleTQwIg0KICApDQpwbHRfY291bnRyeV92c19saWZlRXhwDQpgYGANCiBZb3VyIGV4cGxhbmF0b3J5IHBsb3QgY2xlYXJseSBzaG93cyB0aGUgY291bnRyaWVzIHdpdGggdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCBsaWZlIGV4cGVjdGFuY3kgYW5kIHdvdWxkIGJlIGdyZWF0IGZvciBhIGxheSBhdWRpZW5jZS4NCg==

grahampravall1951.blogspot.com

Source: https://rstudio-pubs-static.s3.amazonaws.com/614151_17aa2ca6247b410ab70a15169f3e828d.html

Postar um comentário for "Why is the Legend Changing From Continuous to Categorical Ggplot"