• 1 R Time Series Visualization Tools
    • 1.1 Refresher on xts and the plot() function Arnaud Amsellem The R Trader
    • 1.2 Other useful visualizing functions
  • 2 Univariate Time Series
  • 3 Multivariate Time Series
  • 4 Case study: Visually selecting a stock that improves your existing portfolio

1 R Time Series Visualization Tools

1.1 Refresher on xts and the plot() function Arnaud Amsellem The R Trader

1.1.1 plot() function - basic parameters

The plot.xts() function is the most useful tool in the R time series data visualization artillery. It is fairly similar to general plotting, but its x-axis contains a time scale. You can use plot() instead of plot.xts() if the object used in the function is an xts object.

Let’s look at a few examples:

# Basic syntax
> plot(mydata)

> # Add title and double thickness of line
> plot(mydata, main = "Stock XYZ", lwd = 2)

> # Add labels for X and Y axes
> plot(mydata, xlab = "X axis", ylab = "Y axis")> 

As you can see, there are a wide variety of parameters for the function allowing endless possibilities. Note that each call of plot() creates an entirely new plot only using the parameters that are defined in that particular call.

Furthermore, to display the first few rows of a dataset mydata to your console, use head(mydata). To display only the names of the columns, use colnames(mydata). You can also select a particular column of a dataset by specifying its title after a dollar sign, like in mydata$mycolumn.

library(readr)
library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:xts㤼㸲:

    first, last

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(xts)
data <- read_table2("stocks_01.csv",
                    col_names = FALSE,
                    skip = 1,
                    col_types = cols(
                      X2 = col_number(),
                      X3 = col_number(),
                      X4 = col_number(),
                      X5 = col_number()
                      ))
data <- rename(data, index = X1, 
       yahoo = X2, 
       microsoft = X3,
       citigroup = X4,
       dow_chemical = X5)
head(data)
ABCDEFGHIJ0123456789
index
<date>
yahoo
<dbl>
microsoft
<dbl>
citigroup
<dbl>
dow_chemical
<dbl>
2015-01-0250.1744.3050153.4525942.48209
2015-01-0549.1343.8975951.7680341.16822
2015-01-0649.2143.2532949.9455640.50662
2015-01-0748.5943.8028450.4085740.44139
2015-01-0850.2345.0914451.1671141.44776
2015-01-0949.7244.7124450.0243741.38254
data$index <- as.Date(data$index)
data <- as.xts(data [, -1], order.by = data$index)
# Display the first few lines of the data
head(data)
           yahoo microsoft citigroup dow_chemical
2015-01-02 50.17  44.30501  53.45259     42.48209
2015-01-05 49.13  43.89759  51.76803     41.16822
2015-01-06 49.21  43.25329  49.94556     40.50662
2015-01-07 48.59  43.80284  50.40857     40.44139
2015-01-08 50.23  45.09144  51.16711     41.44776
2015-01-09 49.72  44.71244  50.02437     41.38254
# Display the column names of the data
colnames(data)
[1] "yahoo"        "microsoft"    "citigroup"    "dow_chemical"
# Plot yahoo data and add title
plot(data$yahoo, main = "yahoo")


# Replot yahoo data with labels for X and Y axes
plot(data$yahoo, main = "yahoo", xlab = "date", ylab = "price")

You can add even more customization with the plot() function using other options. As you saw in the video, the lines() function is especially helpful when you want to modify an existing plot.

Let’s look at another example:

> # Use bars instead of points and add subtitle
> plot(mydata, type = "h", sub = "Subtitle")

> # Triple thickness of line and change color to red
> lines(mydata, col = "red", lwd = 3)
# Plot the second time series and change title
plot(data[ ,2], main = "microsoft")


# Replot with same title, add subtitle, use bars
plot(data[ ,2], main = "microsoft", sub = "Daily closing price since 2015", type = "h")


# Change line color to red
lines(data[ ,2], col = "red")

1.1.2 Control graphic parameters

n R, it is also possible to tailor the window layout using the par() function.

To set up a graphical window for multiple charts with nr rows and nc columns, assign the vector c(nr, nc) to the option mfrow. To adjust the size of the margins and characters in the text, set the appropriate decimal value to to the options mex and cex, respectively. Like plot(), each call to par() only implements the parameters in that particular call.

> # Create 3x1 graphical window
> par(mfrow = c(3, 1))

> # Also reduce margin and character sizes by half
> par(mfrow = c(2, 1), mex = 0.5, cex = 0.5)
# Plot two charts on same graphical window
par(mfrow = c(2 , 1))
plot(data[,1], main = "yahoo")
plot(data[,2], main = "microsoft")


# Replot with reduced margin and character sizes
par(mfrow = c(2 , 1), mex = 0.6, cex = 0.8)

plot(data[,1], main = "yahoo")
plot(data[,2], main = "microsoft")

1.2 Other useful visualizing functions

1.2.1 Adding an extra series to an existing chart

A great way to visually compare two times series is to display them on the same chart with different scales.

Suppose you already have a plot of mydata. As you saw in the video, you can use lines(mydata2) to add a new time series mydata2 to this existing plot. If you want a scale for this time series on the right side of the plot with equally spaced tick marks, use axis(side, at), where side is an integer specifying which side of the plot the axis should be drawn on, and at is set equal to pretty(mydata2).

Finally, to distinguish these two time series, you can add a legend with the legend() function.

> # x specifies location of legend in plot
> legend(x = "bottomright",
         # legend specifies text label(s)
         legend = c("Stock X", "Stock Y"),
         # col specifies color(s)
         col = c("black", "red"),
         # lty specifies line type(s)
         lty = c(1, 1))

Since there are two time series in the plot, some options in legend() are set to a vector of length two.

# Plot the "microsoft" series
plot(data$microsoft, main = "Stock prices since 2015")


# Add the "dow_chemical" series in red
lines(data$dow_chemical, col = "red")

# Add a Y axis on the right side of the chart
axis(side = 4, at = pretty(data$dow_chemical))

# Add a legend in the bottom right corner
legend(x = "topleft",
       legend = c("microsoft", "dow_chemical"),
       col = c("black", "red"),
       lty = c(1, 1))

1.2.2 Highlighting events in a time series

You have also learned that it is possible to use the function abline() to add straight lines through an existing plot. Specifically, you can draw a horizontal line to identify a particular date by setting h to a specific Y value, and a vertical line to identify a particular level by setting v to a specific X value:

> abline(h = NULL, v = NULL, ...)

Recall that the index of an xts object are date objects, so the X values of a plot will also contain dates. In this exercise, you will use indexing as well as as.Date(“YYYY-MM-DD”) and mean() to visually compare the average of the Citigroup stock market prices to its price on January 4, 2016, after it was affected by turbulence in the Chinese stock market.

# Plot the "citigroup" time series
plot.zoo(data$citigroup, main = "Citigroup")

# Create vert_line to identify January 4th, 2016 in citigroup
vert_line <- as.Date("2016-01-04")

# Add a red vertical line using vert_line
abline(v = vert_line, col = "red")

# Create hori_line to identify average price of citigroup
hori_line <- mean(data$citigroup)

# Add a blue horizontal line using hori_line
abline(h = hori_line, col = "blue")

1.2.3 Highlighting a specific period in a time series

To highlight a specific period in a time series, you can display it in the plot in a different background color. The chart.TimeSeries() function in the PerformanceAnalytics package offers a very easy and flexible way of doing this.

Let’s examine some of the arguments of this function:

chart.TimeSeries(R, period.areas, period.color)

R is an xts, time series, or zoo object of asset returns, period.areas are shaded areas specified by a start and end date in a vector of xts date ranges like c(“1926-10/1927-11”), and period.color draws the shaded region in whichever color is specified.

library(PerformanceAnalytics)
package 㤼㸱PerformanceAnalytics㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱PerformanceAnalytics㤼㸲

The following object is masked from 㤼㸱package:graphics㤼㸲:

    legend
# Create period to hold the 3 months of 2015
period <- c("2015-01/2015-03")

# Highlight the first three months of 2015 
chart.TimeSeries(data$citigroup, period.areas = period)


# Highlight the first three months of 2015 in light grey
chart.TimeSeries(data$citigroup, period.areas = period, period.color = "lightgrey")

1.2.4 A fancy stock chart

# Plot the microsoft series
plot(data$microsoft, main = "Dividend date and amount")


# Add the citigroup series
lines(data$citigroup, col = "orange", lwd = 2)

# Add a new y axis for the citigroup series
axis(side = 4, at = pretty(data$citigroup), col = "orange")

you will add a legend to the chart that you just created containing the name of the companies and the dates and values of the latest dividends.

Fill in the pre-written code with the following variables containing the dividend values and dates for both companies:

  • citi_div_value
  • citi_div_date
  • micro_div_value
  • micro_div_date
citi_div_value <- "$0.16"
citi_div_date <- "13 Nov. 2016"
micro_div_value <- "$0.39"
micro_div_date <- "15 Nov. 2016"

Recall that the default color of a plotted line is black, and that the values for legend, col, and lty in legend() should be set to vectors of the same length as the number of time series plotted in your chart.

# Same plot as the previous exercise
plot(data$microsoft, main = "Dividend date and amount")

lines(data$citigroup, col = "orange", lwd = 2)
axis(side = 4, at = pretty(data$citigroup), col = "orange")


# Create the two legend strings
micro <- paste0("Microsoft div. of ", micro_div_value," on ", micro_div_value)
citi <- paste0("Citigroup div. of ", citi_div_value," on ", citi_div_date)

# Create the legend in the bottom right corner
# Same plot as the previous exercise
plot(data$microsoft, main = "Dividend date and amount")

lines(data$citigroup, col = "orange", lwd = 2)
axis(side = 4, at = pretty(data$citigroup), col = "orange")

# Create the two legend strings
micro <- paste0("Microsoft div. of ", micro_div_value," on ", micro_div_date)
citi <- paste0("Citigroup div. of ", citi_div_value," on ", citi_div_date)

# Create the legend in the bottom right corner
legend(x = "bottomright", legend = c(micro, citi), col = c("black", "orange"), lty = c(1, 1))

2 Univariate Time Series

2.1 Univariate time series analysis

2.1.1 Representing a univariate time series

The very first step in the analysis of any time series is to address if the time series have the right mathematical properties to apply the standard statistical framework. If not, you must transform the time series first.

In finance, price series are often transformed to differenced data, making it a return series. In R, the ROC() (which stands for “Rate of Change”) function from the TTR package does this automatically to a price or volume series x:

ROC(x) In this exercise, you will compare plots of the Apple daily prices and Apple daily returns using the stock data contained in data.

data <- read_table2("apple_daily_returns.csv")
Parsed with column specification:
cols(
  `"Index"` = col_date(format = ""),
  `"Apple"` = col_double()
)
data <- rename(data, index = `"Index"`, 
       apple = `"Apple"`)
data$index <- as.Date(data$index)
data <- as.xts(data [, -1], order.by = data$index)
library(TTR)
package 㤼㸱TTR㤼㸲 was built under R version 4.0.3
# Plot Apple's stock price 
plot(data$apple, main = "Apple stock price")

# Create a time series called rtn
rtn <- ROC(data$apple)

# Plot Apple daily price and daily returns 
par(mfrow = c(1, 2))

plot(data$apple, main = "Apple stock price")
plot(rtn)

2.2 Other visualization tools

2.2.1 Histogram of returns

A simple chart of returns does not reveal much about the time series properties; often, data must be displayed in a different format to visualize interesting features.

The density function, represented by the histogram of returns, indicates the most common returns in a time series without taking time into account. In R, these are calculated with the hist() and density() functions.

To create a histogram with 20 buckets, a title, and no Y axis label:

> hist(amazon_stocks,
       breaks = 20,
       main = "AMAZON return distribution",
       xlab = "")

Recall that you can use the lines() function to add a new time series, even with different line properties like color and thickness, to an existing plot.

In this exercise, you will create a histogram of the Apple daily returns data for the last two years contained in rtn.

# Create a histogram of Apple stock returns
hist(rtn,
main = "Apple stock return distribution",
probability = TRUE)

# Add a density line
lines(density(rtn[-1,]))

# Redraw a thicker, red density line
lines(density(rtn[-1,]), lwd = 2, col = "red")

It looks like Apple might have some extreme returns!

2.2.2 Box and whisker plot

A box and whisker plot gives information regarding the shape, variability, and center (or median) of a data set. It is particularly useful for displaying skewed data.

By comparing the data set to a standard normal distribution, you can identify departure from normality (asymmetry, skewness, etc). The lines extending parallel from the boxes are known as whiskers, which are used to indicate variability outside the upper and lower quartiles, i.e. outliers. Those outliers are usually plotted as individual dots that are in-line with whiskers.

use boxplot() to create a horizontal box and whisker plot:

> boxplot(amazon_stocks,
          horizontal = TRUE,
          main = "Amazon return distribution")

In this exercise, you will draw a box and whisker plot for Apple stock returns in rtn.

rtn <- as.data.frame(rnt[-1, ])
# Draw box and whisker plot for the Apple returns
boxplot(rtn,
horizontal = TRUE)


# Draw a box and whisker plot of a normal distribution
boxplot(rnorm(1000),
horizontal = TRUE)

# Redraw both plots on the same graphical window
par(mfrow = c(2, 1))

boxplot(rtn,
horizontal = TRUE)
boxplot(rnorm(1000),
horizontal = TRUE)

Boxplots are useful for quickly getting a feel of the location and variability in your data.

2.2.3 Autocorrelation

Another important piece of information is the relationship between one point in the time series and points that come before it. This is called autocorrelation and it can be displayed as a chart which indicates the correlation between points separated by various time lags.

In R, you can plot the autocorrelation function using acf(), which by default, displays the first 30 lags (i.e. the correlation between points n and n - 1, n and n - 2, n and n - 3 and so on up to 30). The autocorrelogram, or the autocorrelation chart, tells you how any point in the time series is related to its past as well as how significant this relationship is. The significance levels are given by 2 horizontal lines above and below 0.

> acf(amazon_stocks,
      main = "AMAZON return autocorrelations")
# Draw autocorrelation plot
acf(rtn, main = "Apple return autocorrelation")


# Redraw with a maximum lag of 10
acf(rtn, main = "Apple return autocorrelation", lag.max = 10)

Autocorrelation helps you understand time-lagged relationships in your data.

2.2.4 q-q plot

A q-q plot is a plot of the quantiles of one dataset against the quantiles of a second dataset. This is often used to understand if the data matches the standard statistical framework, or a normal distribution.

If the data is normally distributed, the points in the q-q plot follow a straight diagonal line. This is useful to check for normality at a glance but note that it is not an accurate statistical test.

To create a q-q plot using the qqnorm() function, and a reference line for if the data were perfectly normally distributed with qqline():

> qqnorm(amazon_stocks,
         main = "AMAZON return QQ-plot")

> qqline(amazon_stocks,
         col = "red")

In the context of this course, the first dataset is Apple stock return and the second dataset is a standard normal distribution. In this exercise, you will check how Apple stock returns in rtn deviate from a normal distribution.

# Create q-q plot
qqnorm(rtn[[1]],
main = "Apple return QQ-plot")

# Add a red line showing normality
qqline(rtn[[1]], col = "red")

It does not look like Apple returns fit a normal distribution very well in the tails.

2.3 How to use everything we learned so far?

2.3.1 A comprehensive time series diagnostic

Each plotting function that you’ve learned so far provides a different piece of insight about a time series. By putting together the histogram, the box and whisker plot, the autocorrelogram, and the q-q plot, you can gather a lot of useful information about time series behavior.

In this exercise, you will explore the ExxonMobil return data in the rtn series available in your workspace. Draw a histogram of rtn, scale it to a probability density, and add a red line to the plot showing the density of rtn

rtn <- rtn[[1]]
# Draw histogram and add red density line
hist(rtn,
probability = TRUE)
lines(density(rtn),
col = "red")


# Draw box and whisker plot
boxplot(rtn)


# Draw autocorrelogram
acf(rtn)


# Draw q-q plot and add a red line for normality
qqnorm(rtn)
qqline(rtn, col = "red")

To allow a quick and efficient diagnostic, it is often more convenient to display the four charts above on the same graphical window.

# Set up 2x2 graphical window
par(mfrow = c(2, 2))

# Recreate all four plots
hist(rtn, probability = TRUE)
lines(density(rtn), col = "red")

boxplot(rtn)

acf(rtn)

qqnorm(rtn)
qqline(rtn, col = "red")

  1. The best suited tool to identify asymmetry in a time series is the histogram

  2. If a time series is upward sloping, its distribution will be skewed to the right

  3. Outliers in a time series are the points outside the whiskers in a box and whisker plot

3 Multivariate Time Series

3.1 Dealing with higher dimensions

3.1.1 Two time series grouped or stacked

In the first chapter, you learned how to use axis() to plot two lines on the same graphic with different Y scales. Should you want to compare them, however, you may find other kind of graphs to be more insightful. One solution is to plot both time series as barcharts. There are two types:

Grouped barchart: for a single period, there are as many bars as time series Stacked bar chart: for each period, there is a single bar, and each time series is represented by a portion of the bar proportional to the value of the time series at this date (i.e. the total at each period adds up to 100%)

You are provided with a dataset (portfolio) containing the weigths of stocks A (stocka) and B (stockb) in your portfolio for each month in 2016. You will use the barplot() function to create both types of charts.

startDate <- as.Date("2016-01-01")
endDate <- as.Date("2016-12-01")
date <- seq.Date(startDate, endDate, by = "month")
portfolio <- matrix(c(0.1, 0.4, 0.5, 0.5, 0.2, 0.3, 0.7, 0.8, 0.7, 0.2, 
0.1, 0.2, 0.9, 0.6, 0.5, 0.5, 0.8, 0.7, 0.3, 0.2, 0.3, 0.8, 0.9, 
0.8),
ncol = 2)
colnames(portfolio) <- c("stocka", "stockb")
portfolio <- xts(portfolio, order.by = date)
portfolio
           stocka stockb
2016-01-01    0.1    0.9
2016-02-01    0.4    0.6
2016-03-01    0.5    0.5
2016-04-01    0.5    0.5
2016-05-01    0.2    0.8
2016-06-01    0.3    0.7
2016-07-01    0.7    0.3
2016-08-01    0.8    0.2
2016-09-01    0.7    0.3
2016-10-01    0.2    0.8
2016-11-01    0.1    0.9
2016-12-01    0.2    0.8
# Plot stacked barplot
barplot(portfolio)


# Plot grouped barplot
barplot(portfolio,
beside = TRUE)

The two types of barplot display the same information in very different ways.

3.1.2 Visualizing bivariate relationships

If you want to go even further than simply plotting variables and instead investigate whether any relationship exists between 2 variables, you can draw a scatterplot. This is a graph where the values of two variables are plotted along two axes.

The pattern of the resulting points is used to reveal the presence of any correlation; usually, a regression line is added to identify the tendency, if there is any:

An upward sloping regression line indicates a positive linear relationship between A and B (when A goes up B tends to goes up as well) A downward sloping regression line indicates a negative linear relationship between A and B You can draw a scatterplot and then create a regression model with the following functions: plot(x = A, y = B) lm(B ~ A) In this exercise, you will draw a scatterplot and regression line for the return series for the SP500 (sp500) and Citigroup (citi) from January 2015 to January 2017.

library(quantmod)
package 㤼㸱quantmod㤼㸲 was built under R version 4.0.3Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
Version 0.4-0 included new data defaults. See ?getSymbols.
getSymbols(c("^GSPC", "C"), from = "2015-01-01", to = "2017-01-01", src =  "yahoo", adjust =  TRUE)
㤼㸱getSymbols㤼㸲 currently uses auto.assign=TRUE by default, but will
use auto.assign=FALSE in 0.5-0. You will still be able to use
㤼㸱loadSymbols㤼㸲 to automatically load data. getOption("getSymbols.env")
and getOption("getSymbols.auto.assign") will still be checked for
alternate defaults.

This message is shown once per session and may be disabled by setting 
options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.

incomplete final line found by readTableHeader on 'https://query1.finance.yahoo.com/v7/finance/download/^GSPC?period1=-2208988800&period2=1603756800&interval=1d&events=div&crumb=f9HDxFyHZMj'incomplete final line found by readTableHeader on 'https://query1.finance.yahoo.com/v7/finance/download/^GSPC?period1=-2208988800&period2=1603756800&interval=1d&events=split&crumb=f9HDxFyHZMj'incomplete final line found by readTableHeader on 'https://query1.finance.yahoo.com/v7/finance/download/^GSPC?period1=-2208988800&period2=1603756800&interval=1d&events=split&crumb=f9HDxFyHZMj'
[1] "^GSPC" "C"    
sp500 <- ROC(GSPC$GSPC.Adjusted)
citi <- ROC(C$C.Adjusted)
# Draw the scatterplot
plot(x = coredata(sp500), y = coredata(citi))

# Draw a regression line
abline(reg = lm(citi ~ sp500),
lwd = 2,
col = "red")

It looks there is definitely a positive linear relationship between these two variables.

3.2 Multivariate time series

3.2.1 Correlation matrix

What if you want to evaluate the relationship between mutiple time series? The most common tool to use is a correlation matrix, which is a table showing correlation coefficients between pairs of variables. Several types of correlations exist but the most used ones are:

  • Pearson correlation: measures the linear relationship between 2 variables
  • Spearman rank correlation: measures the statistical dependency between the ranking of 2 variables (not necessarily linear) The latter is used when there is no assumption made on the distribution of the data. All this is achieved in R using the function cor(). You can use the method argument to select the desired correlation type. “pearson” is the default method, but you can specify “spearman” as well.

In this exercise, you will calculate the correlation matrix of the data provided in the dataset my_data containing the returns for 5 stocks: ExxonMobile, Citigroup, Microsoft, Dow Chemical and Yahoo.

library(readr)
library(xts)
my_data <- read_csv("stocks_02.csv")
Parsed with column specification:
cols(
  Index = col_date(format = ""),
  sp500 = col_double(),
  citigroup = col_double(),
  microsoft = col_double(),
  apple = col_double(),
  dowchemical = col_double(),
  yahoo = col_double()
)
#my_data$index <- as.Date(my_data$index)
my_data <- as.xts(my_data [, -1], order.by = my_data$Index)

# Create correlation matrix using Pearson method
cor(my_data, method = "pearson")
                sp500 citigroup microsoft     apple dowchemical     yahoo
sp500       1.0000000 0.5097953 0.3743215 0.3576966   0.5217243 0.2900962
citigroup   0.5097953 1.0000000 0.4841408 0.4291841   0.5085190 0.4029490
microsoft   0.3743215 0.4841408 1.0000000 0.5133469   0.3954523 0.4329388
apple       0.3576966 0.4291841 0.5133469 1.0000000   0.3627755 0.3413626
dowchemical 0.5217243 0.5085190 0.3954523 0.3627755   1.0000000 0.2938749
yahoo       0.2900962 0.4029490 0.4329388 0.3413626   0.2938749 1.0000000
# Create correlation matrix using Spearman method
cor(my_data, method = "spearman")
                sp500 citigroup microsoft     apple dowchemical     yahoo
sp500       1.0000000 0.5192579 0.4244237 0.3518853   0.5316235 0.3262037
citigroup   0.5192579 1.0000000 0.4976477 0.4374850   0.5607511 0.3780730
microsoft   0.4244237 0.4976477 1.0000000 0.5128477   0.4684114 0.4448179
apple       0.3518853 0.4374850 0.5128477 1.0000000   0.3681791 0.3680715
dowchemical 0.5316235 0.5607511 0.4684114 0.3681791   1.0000000 0.3464743
yahoo       0.3262037 0.3780730 0.4448179 0.3680715   0.3464743 1.0000000

Notice how the two methods calculate different correlation values.

3.2.2 Scatterplots for multiple pairs of data

In the previous exercise, you saw a numerical representation of the relationship between pairs of data through a correlation matrix. It’s also possible to have a graphical representation of those relationships using scatterplots.

Specifically, the relationship between pairs() of time series is represented by a facetted scatterplot of all pairs at once. This is very convenient for a quick comparison betwen pairs of time series.

In this exercise, you will draw scatterplots of the stock data in my_data from the previous exercise.

# Create scatterplot matrix
pairs(coredata(my_data))


# Create upper panel scatterplot matrix
pairs(coredata(my_data), lower.panel = NULL)

When you have a small number of time series to compare, a scatterplot matrix can be useful to visualize everything at once.

3.2.3 Correlation plot

R offers other ways of displaying the correlation matrix. With the corrplot package, the visualization of correlations is made easier and more powerful by allowing you to represent the correlations with numbers, symbols, colors, and more.

In this exercise, you will use the provided correlation matrix cor_mat and the corrplot() function to draw some correlation charts.

library(corrplot)
package 㤼㸱corrplot㤼㸲 was built under R version 4.0.3corrplot 0.84 loaded
cor_mat <- cor(my_data, method = "pearson")
# Create correlation matrix
corrplot(cor_mat)


# Create correlation matrix with numbers
corrplot(cor_mat, method = "number")


# Create correlation matrix with colors
corrplot(cor_mat, method = "color")


# Create upper triangle correlation matrix
# Create correlation matrix with numbers
corrplot(cor_mat, method = "number", type = "upper")

3.3 Higher dimension time series

3.3.1 Correlation matrix as heatmap

Should you want to check correlations betweens hundreds of time series, representing correlations with numbers is not really helpful - for a dataset of 100 elements, you would have to analyze 10,000 (100 x 100) correlation numbers!

In this case, a heatmap is a better suited tool. A heatmap is a map or diagram in which data values are represented as colors. When using one, it might also be useful to reorder the corelation matrix to make it more readable. You can create heatmaps using corrplot(method = “color”).

In this exercise, you will create some heatmaps with the same correlation matrix cor_mat as from the previous exercise.

# Draw heatmap of cor_mat
corrplot(cor_mat, method = "color")


# Draw upper heatmap
corrplot(cor_mat,
type = "upper", method = "color")

Draw the upper heatmap ordering the matrix using hclust in the order argument

# Draw heatmap of cor_mat
corrplot(cor_mat, method = "color")


# Draw upper heatmap
corrplot(cor_mat,
type = "upper", method = "color")

4 Case study: Visually selecting a stock that improves your existing portfolio

4.1 Case study presentation

4.1.1 Current portfolio description

Your savings are invested in a portfolio made of 3 stocks: Yahoo, Apple and Microsoft. Each stocks has the same weight in the portfolio at 33%. You have some extra cash to invest, but before going any further, you want to gather some information on your existing portfolio.

In this exercise, you are provided with a dataset data containing the value and the return of the portfolio over time, in value and return, respectively.

data <- read_csv("existing_portfolio.csv")
Parsed with column specification:
cols(
  Index = col_date(format = ""),
  value = col_double(),
  return = col_double()
)
data <- as.xts(data [, -1], order.by = data$Index)
# Plot the portfolio value
plot(data$value, main = "Portfolio Value")


# Plot the portfolio return
plot(data$return, main = "Portfolio Return")


# Plot a histogram of portfolio return 
hist(data$return,
probability = TRUE)

# Add a density line
lines(density(data$return),
lwd = 2,
col = "red")

4.2 New stocks

4.2.1 New stocks description

In this exercise, you will review plotting multiple graphs on the same graphical window.

The new dataset data containing four new stocks is available in your workspace:

Goldman Sachs (GS) Coca-Cola (KO) Walt Disney (DIS) Caterpillar (CAT)

data <- read_csv("stocks_03.csv")
Parsed with column specification:
cols(
  Index = col_date(format = ""),
  GS = col_double(),
  KO = col_double(),
  DIS = col_double(),
  CAT = col_double()
)
data <- as.xts(data [, -1], order.by = data$Index)
# Plot the four stocks on the same graphical window
par(mfrow = c(2, 2),
mex = 0.8,
cex = 0.8)
plot(data$GS)
plot(data$KO)
plot(data$DIS)
plot(data$CAT)

Now that you know what the new stocks look like, you want to find out if any of them provide diversification benefits to your existing portfolio. You can do this by looking at the correlation of each stock to our portfolio, visualized through regression lines.

In this exercise, you are provided with four individual series containing the return of the same four stocks:

Goldman Sachs (gs) Coca-Cola (ko) Walt Disney (dis) Caterpillar (cat)

The return of your existing portfolio in portfolio are also available in your workspace. Now it’s your turn to analyze the relationships!

library(TTR)
portfolio <- read_csv("existing_portfolio.csv")
Parsed with column specification:
cols(
  Index = col_date(format = ""),
  value = col_double(),
  return = col_double()
)
portfolio <- as.xts(portfolio [, -1], order.by = portfolio$Index)
gs <- ROC(coredata(data$GS))
gs <- gs[-1]
ko <- ROC(coredata(data$KO))
ko <- ko[-1]
dis <- ROC(coredata(data$DIS))
dis <- dis[-1]
cat <- ROC(coredata(data$CAT))
cat <- cat[-1]
# Draw the scatterplot of gs against the portfolio
plot(x = gs, y = portfolio$return)

# Add a regression line in red
abline(reg = lm(gs ~portfolio$return),
col = "red",
lwd = 2)

# Plot scatterplots and regression lines to a 2x2 window
par(mfrow = c(2, 2))


plot(x = gs, y = portfolio$return)
abline(reg = lm(gs ~ portfolio$return),
col = "red",
lwd = 2)

plot(x = ko, y = portfolio$return)
abline(reg = lm(ko ~ portfolio$return),
col = "red",
lwd = 2)

plot(x = dis, y = portfolio$return)
abline(reg = lm(dis ~ portfolio$return),
col = "red",
lwd = 2)

plot(x = cat, y = portfolio$return)
abline(reg = lm(cat ~ portfolio$return),
col = "red",
lwd = 2)

Coca-Cola seems to provide the most diversification benefit based on low correlation to the portfolio.

4.2.2 Compare old and new portfolios

Great work. You decide to buy stocks in Coca-Cola, and now your portfolio is made of equal proportions of four stocks: Yahoo, Microsoft, Apple and Coca-Cola.

In this exercise, you are given a dataset old.vs.new.portfolio with the following self-explanatory columns:

old.portfolio.value

new.portfolio.value

old.portfolio.rtn

new.portfolio.rtn

old.vs.new.portfolio <- read_csv("old_vs_new_portfolio.csv")
Parsed with column specification:
cols(
  Index = col_date(format = ""),
  old.portfolio.value = col_double(),
  new.portfolio.value = col_double(),
  old.portfolio.rtn = col_double(),
  new.portfolio.rtn = col_double()
)
old.vs.new.portfolio <- as.xts(old.vs.new.portfolio [, -1], order.by = old.vs.new.portfolio$Index)
# Plot new and old portfolio values on same chart
plot(old.vs.new.portfolio$old.portfolio.value)

lines(old.vs.new.portfolio$new.portfolio.value, col = "red")



# Plot density of the new and old portfolio returns on same chart
plot(density(old.vs.new.portfolio$old.portfolio.rtn))
lines(density(old.vs.new.portfolio$new.portfolio.rtn), col = "red")

The new portfolio seems to have less variation based on the density lines.

4.2.3 A more accurate comparison of portfolios

Looking at the value and distribution of returns of your portfolio is a good start, but it doesn’t necessarily tell the whole story. You could obviously look at many other charts and metrics, but ultimately what matters is performance, and specifically periods of poor performance.

The PerformanceAnalytics package provides additional tools to get a finer view of your portfolio. In particular, the charts.PerformanceSummary() function provides a quick and easy way to display the portfolio value, returns, and periods of poor performance, also known as drawdowns.

In this exercise, you will use this new function on the same old and new portfolio data in old.vs.new.portfolio from the previous exercise.

# Draw value, return, drawdowns of old portfolio
charts.PerformanceSummary(old.vs.new.portfolio$old.portfolio.rtn)


# Draw value, return, drawdowns of new portfolio
charts.PerformanceSummary(old.vs.new.portfolio$new.portfolio.rtn)


# Draw both portfolios on same chart
charts.PerformanceSummary(old.vs.new.portfolio[, c(3, 4)])

The new portfolio looks to have a higher cumulative return and lower drawdown for this period of time.

what grounds should you add a new stock to your portfolio?

Correlation to your existing portfolio to assess diversification, return histogram to assess risk and box and whisker plot to assess average return

LS0tDQp0aXRsZTogIlZpc3VhbGl6aW5nIFRpbWUgU2VyaWVzIERhdGEgaW4gUiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCiMgUiBUaW1lIFNlcmllcyBWaXN1YWxpemF0aW9uIFRvb2xzDQoNCiMjIFJlZnJlc2hlciBvbiB4dHMgYW5kIHRoZSBwbG90KCkgZnVuY3Rpb24gQXJuYXVkIEFtc2VsbGVtIFRoZSBSIFRyYWRlcg0KDQojIyMgcGxvdCgpIGZ1bmN0aW9uIC0gYmFzaWMgcGFyYW1ldGVycw0KDQpUaGUgcGxvdC54dHMoKSBmdW5jdGlvbiBpcyB0aGUgbW9zdCB1c2VmdWwgdG9vbCBpbiB0aGUgUiB0aW1lIHNlcmllcyBkYXRhIHZpc3VhbGl6YXRpb24gYXJ0aWxsZXJ5LiBJdCBpcyBmYWlybHkgc2ltaWxhciB0byBnZW5lcmFsIHBsb3R0aW5nLCBidXQgaXRzIHgtYXhpcyBjb250YWlucyBhIHRpbWUgc2NhbGUuIFlvdSBjYW4gdXNlIHBsb3QoKSBpbnN0ZWFkIG9mIHBsb3QueHRzKCkgaWYgdGhlIG9iamVjdCB1c2VkIGluIHRoZSBmdW5jdGlvbiBpcyBhbiB4dHMgb2JqZWN0Lg0KDQpMZXQncyBsb29rIGF0IGEgZmV3IGV4YW1wbGVzOg0KDQogICAgIyBCYXNpYyBzeW50YXgNCiAgICA+IHBsb3QobXlkYXRhKQ0KICAgIA0KICAgID4gIyBBZGQgdGl0bGUgYW5kIGRvdWJsZSB0aGlja25lc3Mgb2YgbGluZQ0KICAgID4gcGxvdChteWRhdGEsIG1haW4gPSAiU3RvY2sgWFlaIiwgbHdkID0gMikNCiAgICANCiAgICA+ICMgQWRkIGxhYmVscyBmb3IgWCBhbmQgWSBheGVzDQogICAgPiBwbG90KG15ZGF0YSwgeGxhYiA9ICJYIGF4aXMiLCB5bGFiID0gIlkgYXhpcyIpPiANCg0KQXMgeW91IGNhbiBzZWUsIHRoZXJlIGFyZSBhIHdpZGUgdmFyaWV0eSBvZiBwYXJhbWV0ZXJzIGZvciB0aGUgZnVuY3Rpb24gYWxsb3dpbmcgZW5kbGVzcyBwb3NzaWJpbGl0aWVzLiBOb3RlIHRoYXQgZWFjaCBjYWxsIG9mIHBsb3QoKSBjcmVhdGVzIGFuIGVudGlyZWx5IG5ldyBwbG90IG9ubHkgdXNpbmcgdGhlIHBhcmFtZXRlcnMgdGhhdCBhcmUgZGVmaW5lZCBpbiB0aGF0IHBhcnRpY3VsYXIgY2FsbC4NCg0KRnVydGhlcm1vcmUsIHRvIGRpc3BsYXkgdGhlIGZpcnN0IGZldyByb3dzIG9mIGEgZGF0YXNldCBteWRhdGEgdG8geW91ciBjb25zb2xlLCB1c2UgaGVhZChteWRhdGEpLiBUbyBkaXNwbGF5IG9ubHkgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zLCB1c2UgY29sbmFtZXMobXlkYXRhKS4gWW91IGNhbiBhbHNvIHNlbGVjdCBhIHBhcnRpY3VsYXIgY29sdW1uIG9mIGEgZGF0YXNldCBieSBzcGVjaWZ5aW5nIGl0cyB0aXRsZSBhZnRlciBhIGRvbGxhciBzaWduLCBsaWtlIGluIG15ZGF0YSRteWNvbHVtbi4NCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoeHRzKQ0KZGF0YSA8LSByZWFkX3RhYmxlMigic3RvY2tzXzAxLmNzdiIsDQogICAgICAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICBza2lwID0gMSwNCiAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scygNCiAgICAgICAgICAgICAgICAgICAgICBYMiA9IGNvbF9udW1iZXIoKSwNCiAgICAgICAgICAgICAgICAgICAgICBYMyA9IGNvbF9udW1iZXIoKSwNCiAgICAgICAgICAgICAgICAgICAgICBYNCA9IGNvbF9udW1iZXIoKSwNCiAgICAgICAgICAgICAgICAgICAgICBYNSA9IGNvbF9udW1iZXIoKQ0KICAgICAgICAgICAgICAgICAgICAgICkpDQpkYXRhIDwtIHJlbmFtZShkYXRhLCBpbmRleCA9IFgxLCANCiAgICAgICB5YWhvbyA9IFgyLCANCiAgICAgICBtaWNyb3NvZnQgPSBYMywNCiAgICAgICBjaXRpZ3JvdXAgPSBYNCwNCiAgICAgICBkb3dfY2hlbWljYWwgPSBYNSkNCmhlYWQoZGF0YSkNCmRhdGEkaW5kZXggPC0gYXMuRGF0ZShkYXRhJGluZGV4KQ0KZGF0YSA8LSBhcy54dHMoZGF0YSBbLCAtMV0sIG9yZGVyLmJ5ID0gZGF0YSRpbmRleCkNCmBgYA0KYGBge3J9DQojIERpc3BsYXkgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiB0aGUgZGF0YQ0KaGVhZChkYXRhKQ0KDQojIERpc3BsYXkgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgZGF0YQ0KY29sbmFtZXMoZGF0YSkNCg0KIyBQbG90IHlhaG9vIGRhdGEgYW5kIGFkZCB0aXRsZQ0KcGxvdChkYXRhJHlhaG9vLCBtYWluID0gInlhaG9vIikNCg0KIyBSZXBsb3QgeWFob28gZGF0YSB3aXRoIGxhYmVscyBmb3IgWCBhbmQgWSBheGVzDQpwbG90KGRhdGEkeWFob28sIG1haW4gPSAieWFob28iLCB4bGFiID0gImRhdGUiLCB5bGFiID0gInByaWNlIikNCmBgYA0KWW91IGNhbiBhZGQgZXZlbiBtb3JlIGN1c3RvbWl6YXRpb24gd2l0aCB0aGUgcGxvdCgpIGZ1bmN0aW9uIHVzaW5nIG90aGVyIG9wdGlvbnMuIEFzIHlvdSBzYXcgaW4gdGhlIHZpZGVvLCB0aGUgbGluZXMoKSBmdW5jdGlvbiBpcyBlc3BlY2lhbGx5IGhlbHBmdWwgd2hlbiB5b3Ugd2FudCB0byBtb2RpZnkgYW4gZXhpc3RpbmcgcGxvdC4NCg0KTGV0J3MgbG9vayBhdCBhbm90aGVyIGV4YW1wbGU6DQoNCiAgICA+ICMgVXNlIGJhcnMgaW5zdGVhZCBvZiBwb2ludHMgYW5kIGFkZCBzdWJ0aXRsZQ0KICAgID4gcGxvdChteWRhdGEsIHR5cGUgPSAiaCIsIHN1YiA9ICJTdWJ0aXRsZSIpDQogICAgDQogICAgPiAjIFRyaXBsZSB0aGlja25lc3Mgb2YgbGluZSBhbmQgY2hhbmdlIGNvbG9yIHRvIHJlZA0KICAgID4gbGluZXMobXlkYXRhLCBjb2wgPSAicmVkIiwgbHdkID0gMykNCg0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgc2Vjb25kIHRpbWUgc2VyaWVzIGFuZCBjaGFuZ2UgdGl0bGUNCnBsb3QoZGF0YVsgLDJdLCBtYWluID0gIm1pY3Jvc29mdCIpDQoNCiMgUmVwbG90IHdpdGggc2FtZSB0aXRsZSwgYWRkIHN1YnRpdGxlLCB1c2UgYmFycw0KcGxvdChkYXRhWyAsMl0sIG1haW4gPSAibWljcm9zb2Z0Iiwgc3ViID0gIkRhaWx5IGNsb3NpbmcgcHJpY2Ugc2luY2UgMjAxNSIsIHR5cGUgPSAiaCIpDQoNCiMgQ2hhbmdlIGxpbmUgY29sb3IgdG8gcmVkDQpsaW5lcyhkYXRhWyAsMl0sIGNvbCA9ICJyZWQiKQ0KYGBgDQojIyMgQ29udHJvbCBncmFwaGljIHBhcmFtZXRlcnMNCg0KbiBSLCBpdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHRhaWxvciB0aGUgd2luZG93IGxheW91dCB1c2luZyB0aGUgcGFyKCkgZnVuY3Rpb24uDQoNClRvIHNldCB1cCBhIGdyYXBoaWNhbCB3aW5kb3cgZm9yIG11bHRpcGxlIGNoYXJ0cyB3aXRoIG5yIHJvd3MgYW5kIG5jIGNvbHVtbnMsIGFzc2lnbiB0aGUgdmVjdG9yIGMobnIsIG5jKSB0byB0aGUgb3B0aW9uIG1mcm93LiBUbyBhZGp1c3QgdGhlIHNpemUgb2YgdGhlIG1hcmdpbnMgYW5kIGNoYXJhY3RlcnMgaW4gdGhlIHRleHQsIHNldCB0aGUgYXBwcm9wcmlhdGUgZGVjaW1hbCB2YWx1ZSB0byB0byB0aGUgb3B0aW9ucyBtZXggYW5kIGNleCwgcmVzcGVjdGl2ZWx5LiBMaWtlIHBsb3QoKSwgZWFjaCBjYWxsIHRvIHBhcigpIG9ubHkgaW1wbGVtZW50cyB0aGUgcGFyYW1ldGVycyBpbiB0aGF0IHBhcnRpY3VsYXIgY2FsbC4NCg0KICAgID4gIyBDcmVhdGUgM3gxIGdyYXBoaWNhbCB3aW5kb3cNCiAgICA+IHBhcihtZnJvdyA9IGMoMywgMSkpDQogICAgDQogICAgPiAjIEFsc28gcmVkdWNlIG1hcmdpbiBhbmQgY2hhcmFjdGVyIHNpemVzIGJ5IGhhbGYNCiAgICA+IHBhcihtZnJvdyA9IGMoMiwgMSksIG1leCA9IDAuNSwgY2V4ID0gMC41KQ0KDQpgYGB7cn0NCiMgUGxvdCB0d28gY2hhcnRzIG9uIHNhbWUgZ3JhcGhpY2FsIHdpbmRvdw0KcGFyKG1mcm93ID0gYygyICwgMSkpDQpwbG90KGRhdGFbLDFdLCBtYWluID0gInlhaG9vIikNCnBsb3QoZGF0YVssMl0sIG1haW4gPSAibWljcm9zb2Z0IikNCg0KDQojIFJlcGxvdCB3aXRoIHJlZHVjZWQgbWFyZ2luIGFuZCBjaGFyYWN0ZXIgc2l6ZXMNCnBhcihtZnJvdyA9IGMoMiAsIDEpLCBtZXggPSAwLjYsIGNleCA9IDAuOCkNCnBsb3QoZGF0YVssMV0sIG1haW4gPSAieWFob28iKQ0KcGxvdChkYXRhWywyXSwgbWFpbiA9ICJtaWNyb3NvZnQiKQ0KYGBgDQojIyBPdGhlciB1c2VmdWwgdmlzdWFsaXppbmcgZnVuY3Rpb25zDQoNCiMjIyBBZGRpbmcgYW4gZXh0cmEgc2VyaWVzIHRvIGFuIGV4aXN0aW5nIGNoYXJ0DQoNCkEgZ3JlYXQgd2F5IHRvIHZpc3VhbGx5IGNvbXBhcmUgdHdvIHRpbWVzIHNlcmllcyBpcyB0byBkaXNwbGF5IHRoZW0gb24gdGhlIHNhbWUgY2hhcnQgd2l0aCBkaWZmZXJlbnQgc2NhbGVzLg0KDQpTdXBwb3NlIHlvdSBhbHJlYWR5IGhhdmUgYSBwbG90IG9mIG15ZGF0YS4gQXMgeW91IHNhdyBpbiB0aGUgdmlkZW8sIHlvdSBjYW4gdXNlIGxpbmVzKG15ZGF0YTIpIHRvIGFkZCBhIG5ldyB0aW1lIHNlcmllcyBteWRhdGEyIHRvIHRoaXMgZXhpc3RpbmcgcGxvdC4gSWYgeW91IHdhbnQgYSBzY2FsZSBmb3IgdGhpcyB0aW1lIHNlcmllcyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgcGxvdCB3aXRoIGVxdWFsbHkgc3BhY2VkIHRpY2sgbWFya3MsIHVzZSBheGlzKHNpZGUsIGF0KSwgd2hlcmUgc2lkZSBpcyBhbiBpbnRlZ2VyIHNwZWNpZnlpbmcgd2hpY2ggc2lkZSBvZiB0aGUgcGxvdCB0aGUgYXhpcyBzaG91bGQgYmUgZHJhd24gb24sIGFuZCBhdCBpcyBzZXQgZXF1YWwgdG8gcHJldHR5KG15ZGF0YTIpLg0KDQpGaW5hbGx5LCB0byBkaXN0aW5ndWlzaCB0aGVzZSB0d28gdGltZSBzZXJpZXMsIHlvdSBjYW4gYWRkIGEgbGVnZW5kIHdpdGggdGhlIGxlZ2VuZCgpIGZ1bmN0aW9uLg0KDQogICAgPiAjIHggc3BlY2lmaWVzIGxvY2F0aW9uIG9mIGxlZ2VuZCBpbiBwbG90DQogICAgPiBsZWdlbmQoeCA9ICJib3R0b21yaWdodCIsDQogICAgICAgICAgICAgIyBsZWdlbmQgc3BlY2lmaWVzIHRleHQgbGFiZWwocykNCiAgICAgICAgICAgICBsZWdlbmQgPSBjKCJTdG9jayBYIiwgIlN0b2NrIFkiKSwNCiAgICAgICAgICAgICAjIGNvbCBzcGVjaWZpZXMgY29sb3IocykNCiAgICAgICAgICAgICBjb2wgPSBjKCJibGFjayIsICJyZWQiKSwNCiAgICAgICAgICAgICAjIGx0eSBzcGVjaWZpZXMgbGluZSB0eXBlKHMpDQogICAgICAgICAgICAgbHR5ID0gYygxLCAxKSkNCg0KU2luY2UgdGhlcmUgYXJlIHR3byB0aW1lIHNlcmllcyBpbiB0aGUgcGxvdCwgc29tZSBvcHRpb25zIGluIGxlZ2VuZCgpIGFyZSBzZXQgdG8gYSB2ZWN0b3Igb2YgbGVuZ3RoIHR3by4NCmBgYHtyfQ0KIyBQbG90IHRoZSAibWljcm9zb2Z0IiBzZXJpZXMNCnBsb3QoZGF0YSRtaWNyb3NvZnQsIG1haW4gPSAiU3RvY2sgcHJpY2VzIHNpbmNlIDIwMTUiKQ0KDQojIEFkZCB0aGUgImRvd19jaGVtaWNhbCIgc2VyaWVzIGluIHJlZA0KbGluZXMoZGF0YSRkb3dfY2hlbWljYWwsIGNvbCA9ICJyZWQiKQ0KDQojIEFkZCBhIFkgYXhpcyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgY2hhcnQNCmF4aXMoc2lkZSA9IDQsIGF0ID0gcHJldHR5KGRhdGEkZG93X2NoZW1pY2FsKSkNCg0KIyBBZGQgYSBsZWdlbmQgaW4gdGhlIGJvdHRvbSByaWdodCBjb3JuZXINCmxlZ2VuZCh4ID0gInRvcGxlZnQiLA0KICAgICAgIGxlZ2VuZCA9IGMoIm1pY3Jvc29mdCIsICJkb3dfY2hlbWljYWwiKSwNCiAgICAgICBjb2wgPSBjKCJibGFjayIsICJyZWQiKSwNCiAgICAgICBsdHkgPSBjKDEsIDEpKQ0KYGBgDQojIyMgSGlnaGxpZ2h0aW5nIGV2ZW50cyBpbiBhIHRpbWUgc2VyaWVzDQoNCllvdSBoYXZlIGFsc28gbGVhcm5lZCB0aGF0IGl0IGlzIHBvc3NpYmxlIHRvIHVzZSB0aGUgZnVuY3Rpb24gYWJsaW5lKCkgdG8gYWRkIHN0cmFpZ2h0IGxpbmVzIHRocm91Z2ggYW4gZXhpc3RpbmcgcGxvdC4gU3BlY2lmaWNhbGx5LCB5b3UgY2FuIGRyYXcgYSBob3Jpem9udGFsIGxpbmUgdG8gaWRlbnRpZnkgYSBwYXJ0aWN1bGFyIGRhdGUgYnkgc2V0dGluZyBoIHRvIGEgc3BlY2lmaWMgWSB2YWx1ZSwgYW5kIGEgdmVydGljYWwgbGluZSB0byBpZGVudGlmeSBhIHBhcnRpY3VsYXIgbGV2ZWwgYnkgc2V0dGluZyB2IHRvIGEgc3BlY2lmaWMgWCB2YWx1ZToNCg0KICAgID4gYWJsaW5lKGggPSBOVUxMLCB2ID0gTlVMTCwgLi4uKQ0KDQpSZWNhbGwgdGhhdCB0aGUgaW5kZXggb2YgYW4geHRzIG9iamVjdCBhcmUgZGF0ZSBvYmplY3RzLCBzbyB0aGUgWCB2YWx1ZXMgb2YgYSBwbG90IHdpbGwgYWxzbyBjb250YWluIGRhdGVzLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCB1c2UgaW5kZXhpbmcgYXMgd2VsbCBhcyBhcy5EYXRlKCJZWVlZLU1NLUREIikgYW5kIG1lYW4oKSB0byB2aXN1YWxseSBjb21wYXJlIHRoZSBhdmVyYWdlIG9mIHRoZSBDaXRpZ3JvdXAgc3RvY2sgbWFya2V0IHByaWNlcyB0byBpdHMgcHJpY2Ugb24gSmFudWFyeSA0LCAyMDE2LCBhZnRlciBpdCB3YXMgYWZmZWN0ZWQgYnkgdHVyYnVsZW5jZSBpbiB0aGUgQ2hpbmVzZSBzdG9jayBtYXJrZXQuDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSAiY2l0aWdyb3VwIiB0aW1lIHNlcmllcw0KcGxvdC56b28oZGF0YSRjaXRpZ3JvdXAsIG1haW4gPSAiQ2l0aWdyb3VwIikNCg0KIyBDcmVhdGUgdmVydF9saW5lIHRvIGlkZW50aWZ5IEphbnVhcnkgNHRoLCAyMDE2IGluIGNpdGlncm91cA0KdmVydF9saW5lIDwtIGFzLkRhdGUoIjIwMTYtMDEtMDQiKQ0KDQojIEFkZCBhIHJlZCB2ZXJ0aWNhbCBsaW5lIHVzaW5nIHZlcnRfbGluZQ0KYWJsaW5lKHYgPSB2ZXJ0X2xpbmUsIGNvbCA9ICJyZWQiKQ0KDQojIENyZWF0ZSBob3JpX2xpbmUgdG8gaWRlbnRpZnkgYXZlcmFnZSBwcmljZSBvZiBjaXRpZ3JvdXANCmhvcmlfbGluZSA8LSBtZWFuKGRhdGEkY2l0aWdyb3VwKQ0KDQojIEFkZCBhIGJsdWUgaG9yaXpvbnRhbCBsaW5lIHVzaW5nIGhvcmlfbGluZQ0KYWJsaW5lKGggPSBob3JpX2xpbmUsIGNvbCA9ICJibHVlIikNCmBgYA0KIyMjIEhpZ2hsaWdodGluZyBhIHNwZWNpZmljIHBlcmlvZCBpbiBhIHRpbWUgc2VyaWVzDQoNClRvIGhpZ2hsaWdodCBhIHNwZWNpZmljIHBlcmlvZCBpbiBhIHRpbWUgc2VyaWVzLCB5b3UgY2FuIGRpc3BsYXkgaXQgaW4gdGhlIHBsb3QgaW4gYSBkaWZmZXJlbnQgYmFja2dyb3VuZCBjb2xvci4gVGhlIGNoYXJ0LlRpbWVTZXJpZXMoKSBmdW5jdGlvbiBpbiB0aGUgUGVyZm9ybWFuY2VBbmFseXRpY3MgcGFja2FnZSBvZmZlcnMgYSB2ZXJ5IGVhc3kgYW5kIGZsZXhpYmxlIHdheSBvZiBkb2luZyB0aGlzLg0KDQpMZXQncyBleGFtaW5lIHNvbWUgb2YgdGhlIGFyZ3VtZW50cyBvZiB0aGlzIGZ1bmN0aW9uOg0KDQogICAgY2hhcnQuVGltZVNlcmllcyhSLCBwZXJpb2QuYXJlYXMsIHBlcmlvZC5jb2xvcikNCiAgICANClIgaXMgYW4geHRzLCB0aW1lIHNlcmllcywgb3Igem9vIG9iamVjdCBvZiBhc3NldCByZXR1cm5zLCBwZXJpb2QuYXJlYXMgYXJlIHNoYWRlZCBhcmVhcyBzcGVjaWZpZWQgYnkgYSBzdGFydCBhbmQgZW5kIGRhdGUgaW4gYSB2ZWN0b3Igb2YgeHRzIGRhdGUgcmFuZ2VzIGxpa2UgYygiMTkyNi0xMC8xOTI3LTExIiksIGFuZCBwZXJpb2QuY29sb3IgZHJhd3MgdGhlIHNoYWRlZCByZWdpb24gaW4gd2hpY2hldmVyIGNvbG9yIGlzIHNwZWNpZmllZC4NCg0KYGBge3J9DQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KIyBDcmVhdGUgcGVyaW9kIHRvIGhvbGQgdGhlIDMgbW9udGhzIG9mIDIwMTUNCnBlcmlvZCA8LSBjKCIyMDE1LTAxLzIwMTUtMDMiKQ0KDQojIEhpZ2hsaWdodCB0aGUgZmlyc3QgdGhyZWUgbW9udGhzIG9mIDIwMTUgDQpjaGFydC5UaW1lU2VyaWVzKGRhdGEkY2l0aWdyb3VwLCBwZXJpb2QuYXJlYXMgPSBwZXJpb2QpDQoNCiMgSGlnaGxpZ2h0IHRoZSBmaXJzdCB0aHJlZSBtb250aHMgb2YgMjAxNSBpbiBsaWdodCBncmV5DQpjaGFydC5UaW1lU2VyaWVzKGRhdGEkY2l0aWdyb3VwLCBwZXJpb2QuYXJlYXMgPSBwZXJpb2QsIHBlcmlvZC5jb2xvciA9ICJsaWdodGdyZXkiKQ0KYGBgDQojIyMgQSBmYW5jeSBzdG9jayBjaGFydA0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgbWljcm9zb2Z0IHNlcmllcw0KcGxvdChkYXRhJG1pY3Jvc29mdCwgbWFpbiA9ICJEaXZpZGVuZCBkYXRlIGFuZCBhbW91bnQiKQ0KDQojIEFkZCB0aGUgY2l0aWdyb3VwIHNlcmllcw0KbGluZXMoZGF0YSRjaXRpZ3JvdXAsIGNvbCA9ICJvcmFuZ2UiLCBsd2QgPSAyKQ0KDQojIEFkZCBhIG5ldyB5IGF4aXMgZm9yIHRoZSBjaXRpZ3JvdXAgc2VyaWVzDQpheGlzKHNpZGUgPSA0LCBhdCA9IHByZXR0eShkYXRhJGNpdGlncm91cCksIGNvbCA9ICJvcmFuZ2UiKQ0KYGBgDQp5b3Ugd2lsbCBhZGQgYSBsZWdlbmQgdG8gdGhlIGNoYXJ0IHRoYXQgeW91IGp1c3QgY3JlYXRlZCBjb250YWluaW5nIHRoZSBuYW1lIG9mIHRoZSBjb21wYW5pZXMgYW5kIHRoZSBkYXRlcyBhbmQgdmFsdWVzIG9mIHRoZSBsYXRlc3QgZGl2aWRlbmRzLg0KDQpGaWxsIGluIHRoZSBwcmUtd3JpdHRlbiBjb2RlIHdpdGggdGhlIGZvbGxvd2luZyB2YXJpYWJsZXMgY29udGFpbmluZyB0aGUgZGl2aWRlbmQgdmFsdWVzIGFuZCBkYXRlcyBmb3IgYm90aCBjb21wYW5pZXM6DQoNCi0gY2l0aV9kaXZfdmFsdWUNCi0gY2l0aV9kaXZfZGF0ZQ0KLSBtaWNyb19kaXZfdmFsdWUNCi0gbWljcm9fZGl2X2RhdGUNCg0KDQpgYGB7cn0NCmNpdGlfZGl2X3ZhbHVlIDwtICIkMC4xNiINCmNpdGlfZGl2X2RhdGUgPC0gIjEzIE5vdi4gMjAxNiINCm1pY3JvX2Rpdl92YWx1ZSA8LSAiJDAuMzkiDQptaWNyb19kaXZfZGF0ZSA8LSAiMTUgTm92LiAyMDE2Ig0KYGBgDQpSZWNhbGwgdGhhdCB0aGUgZGVmYXVsdCBjb2xvciBvZiBhIHBsb3R0ZWQgbGluZSBpcyBibGFjaywgYW5kIHRoYXQgdGhlIHZhbHVlcyBmb3IgbGVnZW5kLCBjb2wsIGFuZCBsdHkgaW4gbGVnZW5kKCkgc2hvdWxkIGJlIHNldCB0byB2ZWN0b3JzIG9mIHRoZSBzYW1lIGxlbmd0aCBhcyB0aGUgbnVtYmVyIG9mIHRpbWUgc2VyaWVzIHBsb3R0ZWQgaW4geW91ciBjaGFydC4NCmBgYHtyfQ0KIyBTYW1lIHBsb3QgYXMgdGhlIHByZXZpb3VzIGV4ZXJjaXNlDQpwbG90KGRhdGEkbWljcm9zb2Z0LCBtYWluID0gIkRpdmlkZW5kIGRhdGUgYW5kIGFtb3VudCIpDQpsaW5lcyhkYXRhJGNpdGlncm91cCwgY29sID0gIm9yYW5nZSIsIGx3ZCA9IDIpDQpheGlzKHNpZGUgPSA0LCBhdCA9IHByZXR0eShkYXRhJGNpdGlncm91cCksIGNvbCA9ICJvcmFuZ2UiKQ0KDQojIENyZWF0ZSB0aGUgdHdvIGxlZ2VuZCBzdHJpbmdzDQptaWNybyA8LSBwYXN0ZTAoIk1pY3Jvc29mdCBkaXYuIG9mICIsIG1pY3JvX2Rpdl92YWx1ZSwiIG9uICIsIG1pY3JvX2Rpdl92YWx1ZSkNCmNpdGkgPC0gcGFzdGUwKCJDaXRpZ3JvdXAgZGl2LiBvZiAiLCBjaXRpX2Rpdl92YWx1ZSwiIG9uICIsIGNpdGlfZGl2X2RhdGUpDQoNCiMgQ3JlYXRlIHRoZSBsZWdlbmQgaW4gdGhlIGJvdHRvbSByaWdodCBjb3JuZXINCiMgU2FtZSBwbG90IGFzIHRoZSBwcmV2aW91cyBleGVyY2lzZQ0KcGxvdChkYXRhJG1pY3Jvc29mdCwgbWFpbiA9ICJEaXZpZGVuZCBkYXRlIGFuZCBhbW91bnQiKQ0KbGluZXMoZGF0YSRjaXRpZ3JvdXAsIGNvbCA9ICJvcmFuZ2UiLCBsd2QgPSAyKQ0KYXhpcyhzaWRlID0gNCwgYXQgPSBwcmV0dHkoZGF0YSRjaXRpZ3JvdXApLCBjb2wgPSAib3JhbmdlIikNCg0KIyBDcmVhdGUgdGhlIHR3byBsZWdlbmQgc3RyaW5ncw0KbWljcm8gPC0gcGFzdGUwKCJNaWNyb3NvZnQgZGl2LiBvZiAiLCBtaWNyb19kaXZfdmFsdWUsIiBvbiAiLCBtaWNyb19kaXZfZGF0ZSkNCmNpdGkgPC0gcGFzdGUwKCJDaXRpZ3JvdXAgZGl2LiBvZiAiLCBjaXRpX2Rpdl92YWx1ZSwiIG9uICIsIGNpdGlfZGl2X2RhdGUpDQoNCiMgQ3JlYXRlIHRoZSBsZWdlbmQgaW4gdGhlIGJvdHRvbSByaWdodCBjb3JuZXINCmxlZ2VuZCh4ID0gImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYyhtaWNybywgY2l0aSksIGNvbCA9IGMoImJsYWNrIiwgIm9yYW5nZSIpLCBsdHkgPSBjKDEsIDEpKQ0KYGBgDQojIFVuaXZhcmlhdGUgVGltZSBTZXJpZXMNCg0KIyMgVW5pdmFyaWF0ZSB0aW1lIHNlcmllcyBhbmFseXNpcw0KDQojIyMgUmVwcmVzZW50aW5nIGEgdW5pdmFyaWF0ZSB0aW1lIHNlcmllcw0KDQpUaGUgdmVyeSBmaXJzdCBzdGVwIGluIHRoZSBhbmFseXNpcyBvZiBhbnkgdGltZSBzZXJpZXMgaXMgdG8gYWRkcmVzcyBpZiB0aGUgdGltZSBzZXJpZXMgaGF2ZSB0aGUgcmlnaHQgbWF0aGVtYXRpY2FsIHByb3BlcnRpZXMgdG8gYXBwbHkgdGhlIHN0YW5kYXJkIHN0YXRpc3RpY2FsIGZyYW1ld29yay4gSWYgbm90LCB5b3UgbXVzdCB0cmFuc2Zvcm0gdGhlIHRpbWUgc2VyaWVzIGZpcnN0Lg0KDQpJbiBmaW5hbmNlLCBwcmljZSBzZXJpZXMgYXJlIG9mdGVuIHRyYW5zZm9ybWVkIHRvIGRpZmZlcmVuY2VkIGRhdGEsIG1ha2luZyBpdCBhIHJldHVybiBzZXJpZXMuIEluIFIsIHRoZSBST0MoKSAod2hpY2ggc3RhbmRzIGZvciAiUmF0ZSBvZiBDaGFuZ2UiKSBmdW5jdGlvbiBmcm9tIHRoZSBUVFIgcGFja2FnZSBkb2VzIHRoaXMgYXV0b21hdGljYWxseSB0byBhIHByaWNlIG9yIHZvbHVtZSBzZXJpZXMgeDoNCg0KUk9DKHgpDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjb21wYXJlIHBsb3RzIG9mIHRoZSBBcHBsZSBkYWlseSBwcmljZXMgYW5kIEFwcGxlIGRhaWx5IHJldHVybnMgdXNpbmcgdGhlIHN0b2NrIGRhdGEgY29udGFpbmVkIGluIGRhdGEuDQpgYGB7cn0NCmRhdGEgPC0gcmVhZF90YWJsZTIoImFwcGxlX2RhaWx5X3JldHVybnMuY3N2IikNCmRhdGEgPC0gcmVuYW1lKGRhdGEsIGluZGV4ID0gYCJJbmRleCJgLCANCiAgICAgICBhcHBsZSA9IGAiQXBwbGUiYCkNCmRhdGEkaW5kZXggPC0gYXMuRGF0ZShkYXRhJGluZGV4KQ0KZGF0YSA8LSBhcy54dHMoZGF0YSBbLCAtMV0sIG9yZGVyLmJ5ID0gZGF0YSRpbmRleCkNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoVFRSKQ0KIyBQbG90IEFwcGxlJ3Mgc3RvY2sgcHJpY2UgDQpwbG90KGRhdGEkYXBwbGUsIG1haW4gPSAiQXBwbGUgc3RvY2sgcHJpY2UiKQ0KDQojIENyZWF0ZSBhIHRpbWUgc2VyaWVzIGNhbGxlZCBydG4NCnJ0biA8LSBST0MoZGF0YSRhcHBsZSkNCg0KIyBQbG90IEFwcGxlIGRhaWx5IHByaWNlIGFuZCBkYWlseSByZXR1cm5zIA0KcGFyKG1mcm93ID0gYygxLCAyKSkNCnBsb3QoZGF0YSRhcHBsZSwgbWFpbiA9ICJBcHBsZSBzdG9jayBwcmljZSIpDQpwbG90KHJ0bikNCmBgYA0KIyMgT3RoZXIgdmlzdWFsaXphdGlvbiB0b29scw0KDQojIyMgSGlzdG9ncmFtIG9mIHJldHVybnMNCg0KQSBzaW1wbGUgY2hhcnQgb2YgcmV0dXJucyBkb2VzIG5vdCByZXZlYWwgbXVjaCBhYm91dCB0aGUgdGltZSBzZXJpZXMgcHJvcGVydGllczsgb2Z0ZW4sIGRhdGEgbXVzdCBiZSBkaXNwbGF5ZWQgaW4gYSBkaWZmZXJlbnQgZm9ybWF0IHRvIHZpc3VhbGl6ZSBpbnRlcmVzdGluZyBmZWF0dXJlcy4NCg0KVGhlIGRlbnNpdHkgZnVuY3Rpb24sIHJlcHJlc2VudGVkIGJ5IHRoZSBoaXN0b2dyYW0gb2YgcmV0dXJucywgaW5kaWNhdGVzIHRoZSBtb3N0IGNvbW1vbiByZXR1cm5zIGluIGEgdGltZSBzZXJpZXMgd2l0aG91dCB0YWtpbmcgdGltZSBpbnRvIGFjY291bnQuIEluIFIsIHRoZXNlIGFyZSBjYWxjdWxhdGVkIHdpdGggdGhlIGhpc3QoKSBhbmQgZGVuc2l0eSgpIGZ1bmN0aW9ucy4NCg0KVG8gY3JlYXRlIGEgaGlzdG9ncmFtIHdpdGggMjAgYnVja2V0cywgYSB0aXRsZSwgYW5kIG5vIFkgYXhpcyBsYWJlbDoNCg0KICAgID4gaGlzdChhbWF6b25fc3RvY2tzLA0KICAgICAgICAgICBicmVha3MgPSAyMCwNCiAgICAgICAgICAgbWFpbiA9ICJBTUFaT04gcmV0dXJuIGRpc3RyaWJ1dGlvbiIsDQogICAgICAgICAgIHhsYWIgPSAiIikNCg0KUmVjYWxsIHRoYXQgeW91IGNhbiB1c2UgdGhlIGxpbmVzKCkgZnVuY3Rpb24gdG8gYWRkIGEgbmV3IHRpbWUgc2VyaWVzLCBldmVuIHdpdGggZGlmZmVyZW50IGxpbmUgcHJvcGVydGllcyBsaWtlIGNvbG9yIGFuZCB0aGlja25lc3MsIHRvIGFuIGV4aXN0aW5nIHBsb3QuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNyZWF0ZSBhIGhpc3RvZ3JhbSBvZiB0aGUgQXBwbGUgZGFpbHkgcmV0dXJucyBkYXRhIGZvciB0aGUgbGFzdCB0d28geWVhcnMgY29udGFpbmVkIGluIHJ0bi4NCg0KYGBge3J9DQojIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiBBcHBsZSBzdG9jayByZXR1cm5zDQpoaXN0KHJ0biwNCm1haW4gPSAiQXBwbGUgc3RvY2sgcmV0dXJuIGRpc3RyaWJ1dGlvbiIsDQpwcm9iYWJpbGl0eSA9IFRSVUUpDQoNCiMgQWRkIGEgZGVuc2l0eSBsaW5lDQpsaW5lcyhkZW5zaXR5KHJ0blstMSxdKSkNCg0KIyBSZWRyYXcgYSB0aGlja2VyLCByZWQgZGVuc2l0eSBsaW5lDQpsaW5lcyhkZW5zaXR5KHJ0blstMSxdKSwgbHdkID0gMiwgY29sID0gInJlZCIpDQpgYGANCkl0IGxvb2tzIGxpa2UgQXBwbGUgbWlnaHQgaGF2ZSBzb21lIGV4dHJlbWUgcmV0dXJucyENCg0KIyMjIEJveCBhbmQgd2hpc2tlciBwbG90DQoNCkEgYm94IGFuZCB3aGlza2VyIHBsb3QgZ2l2ZXMgaW5mb3JtYXRpb24gcmVnYXJkaW5nIHRoZSBzaGFwZSwgdmFyaWFiaWxpdHksIGFuZCBjZW50ZXIgKG9yIG1lZGlhbikgb2YgYSBkYXRhIHNldC4gSXQgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgZGlzcGxheWluZyBza2V3ZWQgZGF0YS4NCg0KQnkgY29tcGFyaW5nIHRoZSBkYXRhIHNldCB0byBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24sIHlvdSBjYW4gaWRlbnRpZnkgZGVwYXJ0dXJlIGZyb20gbm9ybWFsaXR5IChhc3ltbWV0cnksIHNrZXduZXNzLCBldGMpLiBUaGUgbGluZXMgZXh0ZW5kaW5nIHBhcmFsbGVsIGZyb20gdGhlIGJveGVzIGFyZSBrbm93biBhcyB3aGlza2Vycywgd2hpY2ggYXJlIHVzZWQgdG8gaW5kaWNhdGUgdmFyaWFiaWxpdHkgb3V0c2lkZSB0aGUgdXBwZXIgYW5kIGxvd2VyIHF1YXJ0aWxlcywgaS5lLiBvdXRsaWVycy4gVGhvc2Ugb3V0bGllcnMgYXJlIHVzdWFsbHkgcGxvdHRlZCBhcyBpbmRpdmlkdWFsIGRvdHMgdGhhdCBhcmUgaW4tbGluZSB3aXRoIHdoaXNrZXJzLg0KDQp1c2UgYm94cGxvdCgpIHRvIGNyZWF0ZSBhIGhvcml6b250YWwgYm94IGFuZCB3aGlza2VyIHBsb3Q6DQoNCiAgICA+IGJveHBsb3QoYW1hem9uX3N0b2NrcywNCiAgICAgICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsDQogICAgICAgICAgICAgIG1haW4gPSAiQW1hem9uIHJldHVybiBkaXN0cmlidXRpb24iKQ0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBkcmF3IGEgYm94IGFuZCB3aGlza2VyIHBsb3QgZm9yIEFwcGxlIHN0b2NrIHJldHVybnMgaW4gcnRuLg0KYGBge3J9DQpydG4gPC0gYXMuZGF0YS5mcmFtZShybnRbLTEsIF0pDQojIERyYXcgYm94IGFuZCB3aGlza2VyIHBsb3QgZm9yIHRoZSBBcHBsZSByZXR1cm5zDQpib3hwbG90KHJ0biwNCmhvcml6b250YWwgPSBUUlVFKQ0KDQojIERyYXcgYSBib3ggYW5kIHdoaXNrZXIgcGxvdCBvZiBhIG5vcm1hbCBkaXN0cmlidXRpb24NCmJveHBsb3Qocm5vcm0oMTAwMCksDQpob3Jpem9udGFsID0gVFJVRSkNCg0KIyBSZWRyYXcgYm90aCBwbG90cyBvbiB0aGUgc2FtZSBncmFwaGljYWwgd2luZG93DQpwYXIobWZyb3cgPSBjKDIsIDEpKQ0KYm94cGxvdChydG4sDQpob3Jpem9udGFsID0gVFJVRSkNCmJveHBsb3Qocm5vcm0oMTAwMCksDQpob3Jpem9udGFsID0gVFJVRSkNCmBgYA0KQm94cGxvdHMgYXJlIHVzZWZ1bCBmb3IgcXVpY2tseSBnZXR0aW5nIGEgZmVlbCBvZiB0aGUgbG9jYXRpb24gYW5kIHZhcmlhYmlsaXR5IGluIHlvdXIgZGF0YS4NCg0KIyMjIEF1dG9jb3JyZWxhdGlvbg0KDQpBbm90aGVyIGltcG9ydGFudCBwaWVjZSBvZiBpbmZvcm1hdGlvbiBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb25lIHBvaW50IGluIHRoZSB0aW1lIHNlcmllcyBhbmQgcG9pbnRzIHRoYXQgY29tZSBiZWZvcmUgaXQuIFRoaXMgaXMgY2FsbGVkIGF1dG9jb3JyZWxhdGlvbiBhbmQgaXQgY2FuIGJlIGRpc3BsYXllZCBhcyBhIGNoYXJ0IHdoaWNoIGluZGljYXRlcyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBwb2ludHMgc2VwYXJhdGVkIGJ5IHZhcmlvdXMgdGltZSBsYWdzLg0KDQpJbiBSLCB5b3UgY2FuIHBsb3QgdGhlIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbiB1c2luZyBhY2YoKSwgd2hpY2ggYnkgZGVmYXVsdCwgZGlzcGxheXMgdGhlIGZpcnN0IDMwIGxhZ3MgKGkuZS4gdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gcG9pbnRzIG4gYW5kIG4gLSAxLCBuIGFuZCBuIC0gMiwgbiBhbmQgbiAtIDMgYW5kIHNvIG9uIHVwIHRvIDMwKS4gVGhlIGF1dG9jb3JyZWxvZ3JhbSwgb3IgdGhlIGF1dG9jb3JyZWxhdGlvbiBjaGFydCwgdGVsbHMgeW91IGhvdyBhbnkgcG9pbnQgaW4gdGhlIHRpbWUgc2VyaWVzIGlzIHJlbGF0ZWQgdG8gaXRzIHBhc3QgYXMgd2VsbCBhcyBob3cgc2lnbmlmaWNhbnQgdGhpcyByZWxhdGlvbnNoaXAgaXMuIFRoZSBzaWduaWZpY2FuY2UgbGV2ZWxzIGFyZSBnaXZlbiBieSAyIGhvcml6b250YWwgbGluZXMgYWJvdmUgYW5kIGJlbG93IDAuDQoNCiAgICA+IGFjZihhbWF6b25fc3RvY2tzLA0KICAgICAgICAgIG1haW4gPSAiQU1BWk9OIHJldHVybiBhdXRvY29ycmVsYXRpb25zIikNCg0KYGBge3J9DQojIERyYXcgYXV0b2NvcnJlbGF0aW9uIHBsb3QNCmFjZihydG4sIG1haW4gPSAiQXBwbGUgcmV0dXJuIGF1dG9jb3JyZWxhdGlvbiIpDQoNCiMgUmVkcmF3IHdpdGggYSBtYXhpbXVtIGxhZyBvZiAxMA0KYWNmKHJ0biwgbWFpbiA9ICJBcHBsZSByZXR1cm4gYXV0b2NvcnJlbGF0aW9uIiwgbGFnLm1heCA9IDEwKQ0KYGBgDQpBdXRvY29ycmVsYXRpb24gaGVscHMgeW91IHVuZGVyc3RhbmQgdGltZS1sYWdnZWQgcmVsYXRpb25zaGlwcyBpbiB5b3VyIGRhdGEuDQoNCiMjIyBxLXEgcGxvdA0KDQpBIHEtcSBwbG90IGlzIGEgcGxvdCBvZiB0aGUgcXVhbnRpbGVzIG9mIG9uZSBkYXRhc2V0IGFnYWluc3QgdGhlIHF1YW50aWxlcyBvZiBhIHNlY29uZCBkYXRhc2V0LiBUaGlzIGlzIG9mdGVuIHVzZWQgdG8gdW5kZXJzdGFuZCBpZiB0aGUgZGF0YSBtYXRjaGVzIHRoZSBzdGFuZGFyZCBzdGF0aXN0aWNhbCBmcmFtZXdvcmssIG9yIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCg0KSWYgdGhlIGRhdGEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHRoZSBwb2ludHMgaW4gdGhlIHEtcSBwbG90IGZvbGxvdyBhIHN0cmFpZ2h0IGRpYWdvbmFsIGxpbmUuIFRoaXMgaXMgdXNlZnVsIHRvIGNoZWNrIGZvciBub3JtYWxpdHkgYXQgYSBnbGFuY2UgYnV0IG5vdGUgdGhhdCBpdCBpcyBub3QgYW4gYWNjdXJhdGUgc3RhdGlzdGljYWwgdGVzdC4NCg0KVG8gY3JlYXRlIGEgcS1xIHBsb3QgdXNpbmcgdGhlIHFxbm9ybSgpIGZ1bmN0aW9uLCBhbmQgYSByZWZlcmVuY2UgbGluZSBmb3IgaWYgdGhlIGRhdGEgd2VyZSBwZXJmZWN0bHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBxcWxpbmUoKToNCg0KICAgID4gcXFub3JtKGFtYXpvbl9zdG9ja3MsDQogICAgICAgICAgICAgbWFpbiA9ICJBTUFaT04gcmV0dXJuIFFRLXBsb3QiKQ0KICAgIA0KICAgID4gcXFsaW5lKGFtYXpvbl9zdG9ja3MsDQogICAgICAgICAgICAgY29sID0gInJlZCIpDQoNCkluIHRoZSBjb250ZXh0IG9mIHRoaXMgY291cnNlLCB0aGUgZmlyc3QgZGF0YXNldCBpcyBBcHBsZSBzdG9jayByZXR1cm4gYW5kIHRoZSBzZWNvbmQgZGF0YXNldCBpcyBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24uIEluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNoZWNrIGhvdyBBcHBsZSBzdG9jayByZXR1cm5zIGluIHJ0biBkZXZpYXRlIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uLg0KYGBge3J9DQojIENyZWF0ZSBxLXEgcGxvdA0KcXFub3JtKHJ0bltbMV1dLA0KbWFpbiA9ICJBcHBsZSByZXR1cm4gUVEtcGxvdCIpDQoNCiMgQWRkIGEgcmVkIGxpbmUgc2hvd2luZyBub3JtYWxpdHkNCnFxbGluZShydG5bWzFdXSwgY29sID0gInJlZCIpDQpgYGANCkl0IGRvZXMgbm90IGxvb2sgbGlrZSBBcHBsZSByZXR1cm5zIGZpdCBhIG5vcm1hbCBkaXN0cmlidXRpb24gdmVyeSB3ZWxsIGluIHRoZSB0YWlscy4NCg0KIyMgSG93IHRvIHVzZSBldmVyeXRoaW5nIHdlIGxlYXJuZWQgc28gZmFyPw0KDQojIyMgQSBjb21wcmVoZW5zaXZlIHRpbWUgc2VyaWVzIGRpYWdub3N0aWMNCg0KRWFjaCBwbG90dGluZyBmdW5jdGlvbiB0aGF0IHlvdSd2ZSBsZWFybmVkIHNvIGZhciBwcm92aWRlcyBhIGRpZmZlcmVudCBwaWVjZSBvZiBpbnNpZ2h0IGFib3V0IGEgdGltZSBzZXJpZXMuIEJ5IHB1dHRpbmcgdG9nZXRoZXIgdGhlIGhpc3RvZ3JhbSwgdGhlIGJveCBhbmQgd2hpc2tlciBwbG90LCB0aGUgYXV0b2NvcnJlbG9ncmFtLCBhbmQgdGhlIHEtcSBwbG90LCB5b3UgY2FuIGdhdGhlciBhIGxvdCBvZiB1c2VmdWwgaW5mb3JtYXRpb24gYWJvdXQgdGltZSBzZXJpZXMgYmVoYXZpb3IuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGV4cGxvcmUgdGhlIEV4eG9uTW9iaWwgcmV0dXJuIGRhdGEgaW4gdGhlIHJ0biBzZXJpZXMgYXZhaWxhYmxlIGluIHlvdXIgd29ya3NwYWNlLg0KRHJhdyBhIGhpc3RvZ3JhbSBvZiBydG4sIHNjYWxlIGl0IHRvIGEgcHJvYmFiaWxpdHkgZGVuc2l0eSwgYW5kIGFkZCBhIHJlZCBsaW5lIHRvIHRoZSBwbG90IHNob3dpbmcgdGhlIGRlbnNpdHkgb2YgcnRuDQoNCmBgYHtyfQ0KcnRuIDwtIHJ0bltbMV1dDQojIERyYXcgaGlzdG9ncmFtIGFuZCBhZGQgcmVkIGRlbnNpdHkgbGluZQ0KaGlzdChydG4sDQpwcm9iYWJpbGl0eSA9IFRSVUUpDQpsaW5lcyhkZW5zaXR5KHJ0biksDQpjb2wgPSAicmVkIikNCg0KIyBEcmF3IGJveCBhbmQgd2hpc2tlciBwbG90DQpib3hwbG90KHJ0bikNCg0KIyBEcmF3IGF1dG9jb3JyZWxvZ3JhbQ0KYWNmKHJ0bikNCg0KIyBEcmF3IHEtcSBwbG90IGFuZCBhZGQgYSByZWQgbGluZSBmb3Igbm9ybWFsaXR5DQpxcW5vcm0ocnRuKQ0KcXFsaW5lKHJ0biwgY29sID0gInJlZCIpDQoNCmBgYA0KVG8gYWxsb3cgYSBxdWljayBhbmQgZWZmaWNpZW50IGRpYWdub3N0aWMsIGl0IGlzIG9mdGVuIG1vcmUgY29udmVuaWVudCB0byBkaXNwbGF5IHRoZSBmb3VyIGNoYXJ0cyBhYm92ZSBvbiB0aGUgc2FtZSBncmFwaGljYWwgd2luZG93Lg0KYGBge3J9DQojIFNldCB1cCAyeDIgZ3JhcGhpY2FsIHdpbmRvdw0KcGFyKG1mcm93ID0gYygyLCAyKSkNCg0KIyBSZWNyZWF0ZSBhbGwgZm91ciBwbG90cw0KaGlzdChydG4sIHByb2JhYmlsaXR5ID0gVFJVRSkNCmxpbmVzKGRlbnNpdHkocnRuKSwgY29sID0gInJlZCIpDQoNCmJveHBsb3QocnRuKQ0KDQphY2YocnRuKQ0KDQpxcW5vcm0ocnRuKQ0KcXFsaW5lKHJ0biwgY29sID0gInJlZCIpDQpgYGANCkEpIFRoZSBiZXN0IHN1aXRlZCB0b29sIHRvIGlkZW50aWZ5IGFzeW1tZXRyeSBpbiBhIHRpbWUgc2VyaWVzIGlzIHRoZSBoaXN0b2dyYW0NCg0KQikgSWYgYSB0aW1lIHNlcmllcyBpcyB1cHdhcmQgc2xvcGluZywgaXRzIGRpc3RyaWJ1dGlvbiB3aWxsIGJlIHNrZXdlZCB0byB0aGUgcmlnaHQNCg0KQykgT3V0bGllcnMgaW4gYSB0aW1lIHNlcmllcyBhcmUgdGhlIHBvaW50cyBvdXRzaWRlIHRoZSB3aGlza2VycyBpbiBhIGJveCBhbmQgd2hpc2tlciBwbG90DQoNCiMgTXVsdGl2YXJpYXRlIFRpbWUgU2VyaWVzDQoNCiMjIERlYWxpbmcgd2l0aCBoaWdoZXIgZGltZW5zaW9ucw0KDQojIyMgVHdvIHRpbWUgc2VyaWVzIGdyb3VwZWQgb3Igc3RhY2tlZA0KDQpJbiB0aGUgZmlyc3QgY2hhcHRlciwgeW91IGxlYXJuZWQgaG93IHRvIHVzZSBheGlzKCkgdG8gcGxvdCB0d28gbGluZXMgb24gdGhlIHNhbWUgZ3JhcGhpYyB3aXRoIGRpZmZlcmVudCBZIHNjYWxlcy4gU2hvdWxkIHlvdSB3YW50IHRvIGNvbXBhcmUgdGhlbSwgaG93ZXZlciwgeW91IG1heSBmaW5kIG90aGVyIGtpbmQgb2YgZ3JhcGhzIHRvIGJlIG1vcmUgaW5zaWdodGZ1bC4gT25lIHNvbHV0aW9uIGlzIHRvIHBsb3QgYm90aCB0aW1lIHNlcmllcyBhcyBiYXJjaGFydHMuIFRoZXJlIGFyZSB0d28gdHlwZXM6DQoNCkdyb3VwZWQgYmFyY2hhcnQ6IGZvciBhIHNpbmdsZSBwZXJpb2QsIHRoZXJlIGFyZSBhcyBtYW55IGJhcnMgYXMgdGltZSBzZXJpZXMNClN0YWNrZWQgYmFyIGNoYXJ0OiBmb3IgZWFjaCBwZXJpb2QsIHRoZXJlIGlzIGEgc2luZ2xlIGJhciwgYW5kIGVhY2ggdGltZSBzZXJpZXMgaXMgcmVwcmVzZW50ZWQgYnkgYSBwb3J0aW9uIG9mIHRoZSBiYXIgcHJvcG9ydGlvbmFsIHRvIHRoZSB2YWx1ZSBvZiB0aGUgdGltZSBzZXJpZXMgYXQgdGhpcyBkYXRlIChpLmUuIHRoZSB0b3RhbCBhdCBlYWNoIHBlcmlvZCBhZGRzIHVwIHRvIDEwMCUpDQoNCllvdSBhcmUgcHJvdmlkZWQgd2l0aCBhIGRhdGFzZXQgKHBvcnRmb2xpbykgY29udGFpbmluZyB0aGUgd2VpZ3RocyBvZiBzdG9ja3MgQSAoc3RvY2thKSBhbmQgQiAoc3RvY2tiKSBpbiB5b3VyIHBvcnRmb2xpbyBmb3IgZWFjaCBtb250aCBpbiAyMDE2LiBZb3Ugd2lsbCB1c2UgdGhlIGJhcnBsb3QoKSBmdW5jdGlvbiB0byBjcmVhdGUgYm90aCB0eXBlcyBvZiBjaGFydHMuDQpgYGB7cn0NCnN0YXJ0RGF0ZSA8LSBhcy5EYXRlKCIyMDE2LTAxLTAxIikNCmVuZERhdGUgPC0gYXMuRGF0ZSgiMjAxNi0xMi0wMSIpDQpkYXRlIDwtIHNlcS5EYXRlKHN0YXJ0RGF0ZSwgZW5kRGF0ZSwgYnkgPSAibW9udGgiKQ0KcG9ydGZvbGlvIDwtIG1hdHJpeChjKDAuMSwgMC40LCAwLjUsIDAuNSwgMC4yLCAwLjMsIDAuNywgMC44LCAwLjcsIDAuMiwgDQowLjEsIDAuMiwgMC45LCAwLjYsIDAuNSwgMC41LCAwLjgsIDAuNywgMC4zLCAwLjIsIDAuMywgMC44LCAwLjksIA0KMC44KSwNCm5jb2wgPSAyKQ0KY29sbmFtZXMocG9ydGZvbGlvKSA8LSBjKCJzdG9ja2EiLCAic3RvY2tiIikNCnBvcnRmb2xpbyA8LSB4dHMocG9ydGZvbGlvLCBvcmRlci5ieSA9IGRhdGUpDQpwb3J0Zm9saW8NCmBgYA0KYGBge3J9DQojIFBsb3Qgc3RhY2tlZCBiYXJwbG90DQpiYXJwbG90KHBvcnRmb2xpbykNCg0KIyBQbG90IGdyb3VwZWQgYmFycGxvdA0KYmFycGxvdChwb3J0Zm9saW8sDQpiZXNpZGUgPSBUUlVFKQ0KYGBgDQpUaGUgdHdvIHR5cGVzIG9mIGJhcnBsb3QgZGlzcGxheSB0aGUgc2FtZSBpbmZvcm1hdGlvbiBpbiB2ZXJ5IGRpZmZlcmVudCB3YXlzLg0KDQojIyMgVmlzdWFsaXppbmcgYml2YXJpYXRlIHJlbGF0aW9uc2hpcHMNCg0KSWYgeW91IHdhbnQgdG8gZ28gZXZlbiBmdXJ0aGVyIHRoYW4gc2ltcGx5IHBsb3R0aW5nIHZhcmlhYmxlcyBhbmQgaW5zdGVhZCBpbnZlc3RpZ2F0ZSB3aGV0aGVyIGFueSByZWxhdGlvbnNoaXAgZXhpc3RzIGJldHdlZW4gMiB2YXJpYWJsZXMsIHlvdSBjYW4gZHJhdyBhIHNjYXR0ZXJwbG90LiBUaGlzIGlzIGEgZ3JhcGggd2hlcmUgdGhlIHZhbHVlcyBvZiB0d28gdmFyaWFibGVzIGFyZSBwbG90dGVkIGFsb25nIHR3byBheGVzLg0KDQpUaGUgcGF0dGVybiBvZiB0aGUgcmVzdWx0aW5nIHBvaW50cyBpcyB1c2VkIHRvIHJldmVhbCB0aGUgcHJlc2VuY2Ugb2YgYW55IGNvcnJlbGF0aW9uOyB1c3VhbGx5LCBhIHJlZ3Jlc3Npb24gbGluZSBpcyBhZGRlZCB0byBpZGVudGlmeSB0aGUgdGVuZGVuY3ksIGlmIHRoZXJlIGlzIGFueToNCg0KQW4gdXB3YXJkIHNsb3BpbmcgcmVncmVzc2lvbiBsaW5lIGluZGljYXRlcyBhIHBvc2l0aXZlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBBIGFuZCBCICh3aGVuIEEgZ29lcyB1cCBCIHRlbmRzIHRvIGdvZXMgdXAgYXMgd2VsbCkNCkEgZG93bndhcmQgc2xvcGluZyByZWdyZXNzaW9uIGxpbmUgaW5kaWNhdGVzIGEgbmVnYXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEEgYW5kIEINCllvdSBjYW4gZHJhdyBhIHNjYXR0ZXJwbG90IGFuZCB0aGVuIGNyZWF0ZSBhIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uczoNCnBsb3QoeCA9IEEsIHkgPSBCKQ0KbG0oQiB+IEEpDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBkcmF3IGEgc2NhdHRlcnBsb3QgYW5kIHJlZ3Jlc3Npb24gbGluZSBmb3IgdGhlIHJldHVybiBzZXJpZXMgZm9yIHRoZSBTUDUwMCAoc3A1MDApIGFuZCBDaXRpZ3JvdXAgKGNpdGkpIGZyb20gSmFudWFyeSAyMDE1IHRvIEphbnVhcnkgMjAxNy4NCg0KYGBge3J9DQpsaWJyYXJ5KHF1YW50bW9kKQ0KZ2V0U3ltYm9scyhjKCJeR1NQQyIsICJDIiksIGZyb20gPSAiMjAxNS0wMS0wMSIsIHRvID0gIjIwMTctMDEtMDEiLCBzcmMgPSAgInlhaG9vIiwgYWRqdXN0ID0gIFRSVUUpDQpgYGANCmBgYHtyfQ0Kc3A1MDAgPC0gUk9DKEdTUEMkR1NQQy5BZGp1c3RlZCkNCmNpdGkgPC0gUk9DKEMkQy5BZGp1c3RlZCkNCmBgYA0KDQpgYGB7cn0NCiMgRHJhdyB0aGUgc2NhdHRlcnBsb3QNCnBsb3QoeCA9IGNvcmVkYXRhKHNwNTAwKSwgeSA9IGNvcmVkYXRhKGNpdGkpKQ0KDQojIERyYXcgYSByZWdyZXNzaW9uIGxpbmUNCmFibGluZShyZWcgPSBsbShjaXRpIH4gc3A1MDApLA0KbHdkID0gMiwNCmNvbCA9ICJyZWQiKQ0KYGBgDQpJdCBsb29rcyB0aGVyZSBpcyBkZWZpbml0ZWx5IGEgcG9zaXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXMuDQoNCiMjIE11bHRpdmFyaWF0ZSB0aW1lIHNlcmllcw0KDQojIyMgQ29ycmVsYXRpb24gbWF0cml4DQoNCldoYXQgaWYgeW91IHdhbnQgdG8gZXZhbHVhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG11dGlwbGUgdGltZSBzZXJpZXM/IFRoZSBtb3N0IGNvbW1vbiB0b29sIHRvIHVzZSBpcyBhIGNvcnJlbGF0aW9uIG1hdHJpeCwgd2hpY2ggaXMgYSB0YWJsZSBzaG93aW5nIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBiZXR3ZWVuIHBhaXJzIG9mIHZhcmlhYmxlcy4gU2V2ZXJhbCB0eXBlcyBvZiBjb3JyZWxhdGlvbnMgZXhpc3QgYnV0IHRoZSBtb3N0IHVzZWQgb25lcyBhcmU6DQoNCi0JUGVhcnNvbiBjb3JyZWxhdGlvbjogbWVhc3VyZXMgdGhlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiAyIHZhcmlhYmxlcw0KLQlTcGVhcm1hbiByYW5rIGNvcnJlbGF0aW9uOiBtZWFzdXJlcyB0aGUgc3RhdGlzdGljYWwgZGVwZW5kZW5jeSBiZXR3ZWVuIHRoZSByYW5raW5nIG9mIDIgdmFyaWFibGVzIChub3QgbmVjZXNzYXJpbHkgbGluZWFyKQ0KVGhlIGxhdHRlciBpcyB1c2VkIHdoZW4gdGhlcmUgaXMgbm8gYXNzdW1wdGlvbiBtYWRlIG9uIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuIEFsbCB0aGlzIGlzIGFjaGlldmVkIGluIFIgdXNpbmcgdGhlIGZ1bmN0aW9uIGNvcigpLiBZb3UgY2FuIHVzZSB0aGUgbWV0aG9kIGFyZ3VtZW50IHRvIHNlbGVjdCB0aGUgZGVzaXJlZCBjb3JyZWxhdGlvbiB0eXBlLiAicGVhcnNvbiIgaXMgdGhlIGRlZmF1bHQgbWV0aG9kLCBidXQgeW91IGNhbiBzcGVjaWZ5ICJzcGVhcm1hbiIgYXMgd2VsbC4NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgY2FsY3VsYXRlIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggb2YgdGhlIGRhdGEgcHJvdmlkZWQgaW4gdGhlIGRhdGFzZXQgbXlfZGF0YSBjb250YWluaW5nIHRoZSByZXR1cm5zIGZvciA1IHN0b2NrczogRXh4b25Nb2JpbGUsIENpdGlncm91cCwgTWljcm9zb2Z0LCBEb3cgQ2hlbWljYWwgYW5kIFlhaG9vLg0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHh0cykNCm15X2RhdGEgPC0gcmVhZF9jc3YoInN0b2Nrc18wMi5jc3YiKQ0KI215X2RhdGEkaW5kZXggPC0gYXMuRGF0ZShteV9kYXRhJGluZGV4KQ0KbXlfZGF0YSA8LSBhcy54dHMobXlfZGF0YSBbLCAtMV0sIG9yZGVyLmJ5ID0gbXlfZGF0YSRJbmRleCkNCmBgYA0KDQpgYGB7cn0NCg0KIyBDcmVhdGUgY29ycmVsYXRpb24gbWF0cml4IHVzaW5nIFBlYXJzb24gbWV0aG9kDQpjb3IobXlfZGF0YSwgbWV0aG9kID0gInBlYXJzb24iKQ0KDQojIENyZWF0ZSBjb3JyZWxhdGlvbiBtYXRyaXggdXNpbmcgU3BlYXJtYW4gbWV0aG9kDQpjb3IobXlfZGF0YSwgbWV0aG9kID0gInNwZWFybWFuIikNCg0KYGBgDQpOb3RpY2UgaG93IHRoZSB0d28gbWV0aG9kcyBjYWxjdWxhdGUgZGlmZmVyZW50IGNvcnJlbGF0aW9uIHZhbHVlcy4NCg0KIyMjIFNjYXR0ZXJwbG90cyBmb3IgbXVsdGlwbGUgcGFpcnMgb2YgZGF0YQ0KDQpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHlvdSBzYXcgYSBudW1lcmljYWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHBhaXJzIG9mIGRhdGEgdGhyb3VnaCBhIGNvcnJlbGF0aW9uIG1hdHJpeC4gSXQncyBhbHNvIHBvc3NpYmxlIHRvIGhhdmUgYSBncmFwaGljYWwgcmVwcmVzZW50YXRpb24gb2YgdGhvc2UgcmVsYXRpb25zaGlwcyB1c2luZyBzY2F0dGVycGxvdHMuDQoNClNwZWNpZmljYWxseSwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHBhaXJzKCkgb2YgdGltZSBzZXJpZXMgaXMgcmVwcmVzZW50ZWQgYnkgYSBmYWNldHRlZCBzY2F0dGVycGxvdCBvZiBhbGwgcGFpcnMgYXQgb25jZS4gVGhpcyBpcyB2ZXJ5IGNvbnZlbmllbnQgZm9yIGEgcXVpY2sgY29tcGFyaXNvbiBiZXR3ZW4gcGFpcnMgb2YgdGltZSBzZXJpZXMuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGRyYXcgc2NhdHRlcnBsb3RzIG9mIHRoZSBzdG9jayBkYXRhIGluIG15X2RhdGEgZnJvbSB0aGUgcHJldmlvdXMgZXhlcmNpc2UuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgc2NhdHRlcnBsb3QgbWF0cml4DQpwYWlycyhjb3JlZGF0YShteV9kYXRhKSkNCg0KIyBDcmVhdGUgdXBwZXIgcGFuZWwgc2NhdHRlcnBsb3QgbWF0cml4DQpwYWlycyhjb3JlZGF0YShteV9kYXRhKSwgbG93ZXIucGFuZWwgPSBOVUxMKQ0KYGBgDQpXaGVuIHlvdSBoYXZlIGEgc21hbGwgbnVtYmVyIG9mIHRpbWUgc2VyaWVzIHRvIGNvbXBhcmUsIGEgc2NhdHRlcnBsb3QgbWF0cml4IGNhbiBiZSB1c2VmdWwgdG8gdmlzdWFsaXplIGV2ZXJ5dGhpbmcgYXQgb25jZS4NCg0KIyMjIENvcnJlbGF0aW9uIHBsb3QNCg0KUiBvZmZlcnMgb3RoZXIgd2F5cyBvZiBkaXNwbGF5aW5nIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXguIFdpdGggdGhlIGNvcnJwbG90IHBhY2thZ2UsIHRoZSB2aXN1YWxpemF0aW9uIG9mIGNvcnJlbGF0aW9ucyBpcyBtYWRlIGVhc2llciBhbmQgbW9yZSBwb3dlcmZ1bCBieSBhbGxvd2luZyB5b3UgdG8gcmVwcmVzZW50IHRoZSBjb3JyZWxhdGlvbnMgd2l0aCBudW1iZXJzLCBzeW1ib2xzLCBjb2xvcnMsIGFuZCBtb3JlLg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCB1c2UgdGhlIHByb3ZpZGVkIGNvcnJlbGF0aW9uIG1hdHJpeCBjb3JfbWF0IGFuZCB0aGUgY29ycnBsb3QoKSBmdW5jdGlvbiB0byBkcmF3IHNvbWUgY29ycmVsYXRpb24gY2hhcnRzLg0KDQpgYGB7cn0NCmxpYnJhcnkoY29ycnBsb3QpDQpjb3JfbWF0IDwtIGNvcihteV9kYXRhLCBtZXRob2QgPSAicGVhcnNvbiIpDQojIENyZWF0ZSBjb3JyZWxhdGlvbiBtYXRyaXgNCmNvcnJwbG90KGNvcl9tYXQpDQoNCiMgQ3JlYXRlIGNvcnJlbGF0aW9uIG1hdHJpeCB3aXRoIG51bWJlcnMNCmNvcnJwbG90KGNvcl9tYXQsIG1ldGhvZCA9ICJudW1iZXIiKQ0KDQojIENyZWF0ZSBjb3JyZWxhdGlvbiBtYXRyaXggd2l0aCBjb2xvcnMNCmNvcnJwbG90KGNvcl9tYXQsIG1ldGhvZCA9ICJjb2xvciIpDQoNCiMgQ3JlYXRlIHVwcGVyIHRyaWFuZ2xlIGNvcnJlbGF0aW9uIG1hdHJpeA0KIyBDcmVhdGUgY29ycmVsYXRpb24gbWF0cml4IHdpdGggbnVtYmVycw0KY29ycnBsb3QoY29yX21hdCwgbWV0aG9kID0gIm51bWJlciIsIHR5cGUgPSAidXBwZXIiKQ0KYGBgDQojIyBIaWdoZXIgZGltZW5zaW9uIHRpbWUgc2VyaWVzDQoNCiMjIyBDb3JyZWxhdGlvbiBtYXRyaXggYXMgaGVhdG1hcA0KDQpTaG91bGQgeW91IHdhbnQgdG8gY2hlY2sgY29ycmVsYXRpb25zIGJldHdlZW5zIGh1bmRyZWRzIG9mIHRpbWUgc2VyaWVzLCByZXByZXNlbnRpbmcgY29ycmVsYXRpb25zIHdpdGggbnVtYmVycyBpcyBub3QgcmVhbGx5IGhlbHBmdWwgLSBmb3IgYSBkYXRhc2V0IG9mIDEwMCBlbGVtZW50cywgeW91IHdvdWxkIGhhdmUgdG8gYW5hbHl6ZSAxMCwwMDAgKDEwMCB4IDEwMCkgY29ycmVsYXRpb24gbnVtYmVycyENCg0KSW4gdGhpcyBjYXNlLCBhIGhlYXRtYXAgaXMgYSBiZXR0ZXIgc3VpdGVkIHRvb2wuIEEgaGVhdG1hcCBpcyBhIG1hcCBvciBkaWFncmFtIGluIHdoaWNoIGRhdGEgdmFsdWVzIGFyZSByZXByZXNlbnRlZCBhcyBjb2xvcnMuIFdoZW4gdXNpbmcgb25lLCBpdCBtaWdodCBhbHNvIGJlIHVzZWZ1bCB0byByZW9yZGVyIHRoZSBjb3JlbGF0aW9uIG1hdHJpeCB0byBtYWtlIGl0IG1vcmUgcmVhZGFibGUuIFlvdSBjYW4gY3JlYXRlIGhlYXRtYXBzIHVzaW5nIGNvcnJwbG90KG1ldGhvZCA9ICJjb2xvciIpLg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjcmVhdGUgc29tZSBoZWF0bWFwcyB3aXRoIHRoZSBzYW1lIGNvcnJlbGF0aW9uIG1hdHJpeCBjb3JfbWF0IGFzIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLg0KYGBge3J9DQojIERyYXcgaGVhdG1hcCBvZiBjb3JfbWF0DQpjb3JycGxvdChjb3JfbWF0LCBtZXRob2QgPSAiY29sb3IiKQ0KDQojIERyYXcgdXBwZXIgaGVhdG1hcA0KY29ycnBsb3QoY29yX21hdCwNCnR5cGUgPSAidXBwZXIiLCBtZXRob2QgPSAiY29sb3IiKQ0KYGBgDQpEcmF3IHRoZSB1cHBlciBoZWF0bWFwIG9yZGVyaW5nIHRoZSBtYXRyaXggdXNpbmcgaGNsdXN0IGluIHRoZSBvcmRlciBhcmd1bWVudA0KYGBge3J9DQojIERyYXcgaGVhdG1hcCBvZiBjb3JfbWF0DQpjb3JycGxvdChjb3JfbWF0LCBtZXRob2QgPSAiY29sb3IiKQ0KDQojIERyYXcgdXBwZXIgaGVhdG1hcA0KY29ycnBsb3QoY29yX21hdCwNCnR5cGUgPSAidXBwZXIiLCBtZXRob2QgPSAiY29sb3IiKQ0KYGBgDQojIENhc2Ugc3R1ZHk6IFZpc3VhbGx5IHNlbGVjdGluZyBhIHN0b2NrIHRoYXQgaW1wcm92ZXMgeW91ciBleGlzdGluZyBwb3J0Zm9saW8NCg0KIyMgQ2FzZSBzdHVkeSBwcmVzZW50YXRpb24NCg0KIyMjIEN1cnJlbnQgcG9ydGZvbGlvIGRlc2NyaXB0aW9uDQoNCllvdXIgc2F2aW5ncyBhcmUgaW52ZXN0ZWQgaW4gYSBwb3J0Zm9saW8gbWFkZSBvZiAzIHN0b2NrczogWWFob28sIEFwcGxlIGFuZCBNaWNyb3NvZnQuIEVhY2ggc3RvY2tzIGhhcyB0aGUgc2FtZSB3ZWlnaHQgaW4gdGhlIHBvcnRmb2xpbyBhdCAzMyUuIFlvdSBoYXZlIHNvbWUgZXh0cmEgY2FzaCB0byBpbnZlc3QsIGJ1dCBiZWZvcmUgZ29pbmcgYW55IGZ1cnRoZXIsIHlvdSB3YW50IHRvIGdhdGhlciBzb21lIGluZm9ybWF0aW9uIG9uIHlvdXIgZXhpc3RpbmcgcG9ydGZvbGlvLg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3UgYXJlIHByb3ZpZGVkIHdpdGggYSBkYXRhc2V0IGRhdGEgY29udGFpbmluZyB0aGUgdmFsdWUgYW5kIHRoZSByZXR1cm4gb2YgdGhlIHBvcnRmb2xpbyBvdmVyIHRpbWUsIGluIHZhbHVlIGFuZCByZXR1cm4sIHJlc3BlY3RpdmVseS4NCmBgYHtyfQ0KZGF0YSA8LSByZWFkX2NzdigiZXhpc3RpbmdfcG9ydGZvbGlvLmNzdiIpDQpkYXRhIDwtIGFzLnh0cyhkYXRhIFssIC0xXSwgb3JkZXIuYnkgPSBkYXRhJEluZGV4KQ0KYGBgDQpgYGB7cn0NCiMgUGxvdCB0aGUgcG9ydGZvbGlvIHZhbHVlDQpwbG90KGRhdGEkdmFsdWUsIG1haW4gPSAiUG9ydGZvbGlvIFZhbHVlIikNCg0KIyBQbG90IHRoZSBwb3J0Zm9saW8gcmV0dXJuDQpwbG90KGRhdGEkcmV0dXJuLCBtYWluID0gIlBvcnRmb2xpbyBSZXR1cm4iKQ0KDQojIFBsb3QgYSBoaXN0b2dyYW0gb2YgcG9ydGZvbGlvIHJldHVybiANCmhpc3QoZGF0YSRyZXR1cm4sDQpwcm9iYWJpbGl0eSA9IFRSVUUpDQoNCiMgQWRkIGEgZGVuc2l0eSBsaW5lDQpsaW5lcyhkZW5zaXR5KGRhdGEkcmV0dXJuKSwNCmx3ZCA9IDIsDQpjb2wgPSAicmVkIikNCmBgYA0KIyMgTmV3IHN0b2Nrcw0KDQojIyMgTmV3IHN0b2NrcyBkZXNjcmlwdGlvbg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCByZXZpZXcgcGxvdHRpbmcgbXVsdGlwbGUgZ3JhcGhzIG9uIHRoZSBzYW1lIGdyYXBoaWNhbCB3aW5kb3cuDQoNClRoZSBuZXcgZGF0YXNldCBkYXRhIGNvbnRhaW5pbmcgZm91ciBuZXcgc3RvY2tzIGlzIGF2YWlsYWJsZSBpbiB5b3VyIHdvcmtzcGFjZToNCg0KR29sZG1hbiBTYWNocyAoR1MpDQpDb2NhLUNvbGEgKEtPKQ0KV2FsdCBEaXNuZXkgKERJUykNCkNhdGVycGlsbGFyIChDQVQpDQoNCmBgYHtyfQ0KZGF0YSA8LSByZWFkX2Nzdigic3RvY2tzXzAzLmNzdiIpDQpkYXRhIDwtIGFzLnh0cyhkYXRhIFssIC0xXSwgb3JkZXIuYnkgPSBkYXRhJEluZGV4KQ0KYGBgDQpgYGB7cn0NCiMgUGxvdCB0aGUgZm91ciBzdG9ja3Mgb24gdGhlIHNhbWUgZ3JhcGhpY2FsIHdpbmRvdw0KcGFyKG1mcm93ID0gYygyLCAyKSwNCm1leCA9IDAuOCwNCmNleCA9IDAuOCkNCnBsb3QoZGF0YSRHUykNCnBsb3QoZGF0YSRLTykNCnBsb3QoZGF0YSRESVMpDQpwbG90KGRhdGEkQ0FUKQ0KYGBgDQpOb3cgdGhhdCB5b3Uga25vdyB3aGF0IHRoZSBuZXcgc3RvY2tzIGxvb2sgbGlrZSwgeW91IHdhbnQgdG8gZmluZCBvdXQgaWYgYW55IG9mIHRoZW0gcHJvdmlkZSBkaXZlcnNpZmljYXRpb24gYmVuZWZpdHMgdG8geW91ciBleGlzdGluZyBwb3J0Zm9saW8uIFlvdSBjYW4gZG8gdGhpcyBieSBsb29raW5nIGF0IHRoZSBjb3JyZWxhdGlvbiBvZiBlYWNoIHN0b2NrIHRvIG91ciBwb3J0Zm9saW8sIHZpc3VhbGl6ZWQgdGhyb3VnaCByZWdyZXNzaW9uIGxpbmVzLg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3UgYXJlIHByb3ZpZGVkIHdpdGggZm91ciBpbmRpdmlkdWFsIHNlcmllcyBjb250YWluaW5nIHRoZSByZXR1cm4gb2YgdGhlIHNhbWUgZm91ciBzdG9ja3M6DQoNCkdvbGRtYW4gU2FjaHMgKGdzKQ0KQ29jYS1Db2xhIChrbykNCldhbHQgRGlzbmV5IChkaXMpDQpDYXRlcnBpbGxhciAoY2F0KQ0KDQpUaGUgcmV0dXJuIG9mIHlvdXIgZXhpc3RpbmcgcG9ydGZvbGlvIGluIHBvcnRmb2xpbyBhcmUgYWxzbyBhdmFpbGFibGUgaW4geW91ciB3b3Jrc3BhY2UuIE5vdyBpdCdzIHlvdXIgdHVybiB0byBhbmFseXplIHRoZSByZWxhdGlvbnNoaXBzIQ0KDQpgYGB7cn0NCmxpYnJhcnkoVFRSKQ0KcG9ydGZvbGlvIDwtIHJlYWRfY3N2KCJleGlzdGluZ19wb3J0Zm9saW8uY3N2IikNCnBvcnRmb2xpbyA8LSBhcy54dHMocG9ydGZvbGlvIFssIC0xXSwgb3JkZXIuYnkgPSBwb3J0Zm9saW8kSW5kZXgpDQpncyA8LSBST0MoY29yZWRhdGEoZGF0YSRHUykpDQpncyA8LSBnc1stMV0NCmtvIDwtIFJPQyhjb3JlZGF0YShkYXRhJEtPKSkNCmtvIDwtIGtvWy0xXQ0KZGlzIDwtIFJPQyhjb3JlZGF0YShkYXRhJERJUykpDQpkaXMgPC0gZGlzWy0xXQ0KY2F0IDwtIFJPQyhjb3JlZGF0YShkYXRhJENBVCkpDQpjYXQgPC0gY2F0Wy0xXQ0KYGBgDQpgYGB7cn0NCiMgRHJhdyB0aGUgc2NhdHRlcnBsb3Qgb2YgZ3MgYWdhaW5zdCB0aGUgcG9ydGZvbGlvDQpwbG90KHggPSBncywgeSA9IHBvcnRmb2xpbyRyZXR1cm4pDQoNCiMgQWRkIGEgcmVncmVzc2lvbiBsaW5lIGluIHJlZA0KYWJsaW5lKHJlZyA9IGxtKGdzIH5wb3J0Zm9saW8kcmV0dXJuKSwNCmNvbCA9ICJyZWQiLA0KbHdkID0gMikNCg0KIyBQbG90IHNjYXR0ZXJwbG90cyBhbmQgcmVncmVzc2lvbiBsaW5lcyB0byBhIDJ4MiB3aW5kb3cNCnBhcihtZnJvdyA9IGMoMiwgMikpDQoNCnBsb3QoeCA9IGdzLCB5ID0gcG9ydGZvbGlvJHJldHVybikNCmFibGluZShyZWcgPSBsbShncyB+IHBvcnRmb2xpbyRyZXR1cm4pLA0KY29sID0gInJlZCIsDQpsd2QgPSAyKQ0KDQpwbG90KHggPSBrbywgeSA9IHBvcnRmb2xpbyRyZXR1cm4pDQphYmxpbmUocmVnID0gbG0oa28gfiBwb3J0Zm9saW8kcmV0dXJuKSwNCmNvbCA9ICJyZWQiLA0KbHdkID0gMikNCg0KcGxvdCh4ID0gZGlzLCB5ID0gcG9ydGZvbGlvJHJldHVybikNCmFibGluZShyZWcgPSBsbShkaXMgfiBwb3J0Zm9saW8kcmV0dXJuKSwNCmNvbCA9ICJyZWQiLA0KbHdkID0gMikNCg0KcGxvdCh4ID0gY2F0LCB5ID0gcG9ydGZvbGlvJHJldHVybikNCmFibGluZShyZWcgPSBsbShjYXQgfiBwb3J0Zm9saW8kcmV0dXJuKSwNCmNvbCA9ICJyZWQiLA0KbHdkID0gMikNCmBgYA0KQ29jYS1Db2xhIHNlZW1zIHRvIHByb3ZpZGUgdGhlIG1vc3QgZGl2ZXJzaWZpY2F0aW9uIGJlbmVmaXQgYmFzZWQgb24gbG93IGNvcnJlbGF0aW9uIHRvIHRoZSBwb3J0Zm9saW8uDQoNCiMjIyBDb21wYXJlIG9sZCBhbmQgbmV3IHBvcnRmb2xpb3MNCg0KR3JlYXQgd29yay4gWW91IGRlY2lkZSB0byBidXkgc3RvY2tzIGluIENvY2EtQ29sYSwgYW5kIG5vdyB5b3VyIHBvcnRmb2xpbyBpcyBtYWRlIG9mIGVxdWFsIHByb3BvcnRpb25zIG9mIGZvdXIgc3RvY2tzOiBZYWhvbywgTWljcm9zb2Z0LCBBcHBsZSBhbmQgQ29jYS1Db2xhLg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3UgYXJlIGdpdmVuIGEgZGF0YXNldCBvbGQudnMubmV3LnBvcnRmb2xpbyB3aXRoIHRoZSBmb2xsb3dpbmcgc2VsZi1leHBsYW5hdG9yeSBjb2x1bW5zOg0KDQpvbGQucG9ydGZvbGlvLnZhbHVlDQoNCm5ldy5wb3J0Zm9saW8udmFsdWUNCg0Kb2xkLnBvcnRmb2xpby5ydG4NCg0KbmV3LnBvcnRmb2xpby5ydG4NCg0KYGBge3J9DQpvbGQudnMubmV3LnBvcnRmb2xpbyA8LSByZWFkX2Nzdigib2xkX3ZzX25ld19wb3J0Zm9saW8uY3N2IikNCm9sZC52cy5uZXcucG9ydGZvbGlvIDwtIGFzLnh0cyhvbGQudnMubmV3LnBvcnRmb2xpbyBbLCAtMV0sIG9yZGVyLmJ5ID0gb2xkLnZzLm5ldy5wb3J0Zm9saW8kSW5kZXgpDQpgYGANCg0KYGBge3J9DQojIFBsb3QgbmV3IGFuZCBvbGQgcG9ydGZvbGlvIHZhbHVlcyBvbiBzYW1lIGNoYXJ0DQpwbG90KG9sZC52cy5uZXcucG9ydGZvbGlvJG9sZC5wb3J0Zm9saW8udmFsdWUpDQpsaW5lcyhvbGQudnMubmV3LnBvcnRmb2xpbyRuZXcucG9ydGZvbGlvLnZhbHVlLCBjb2wgPSAicmVkIikNCg0KDQojIFBsb3QgZGVuc2l0eSBvZiB0aGUgbmV3IGFuZCBvbGQgcG9ydGZvbGlvIHJldHVybnMgb24gc2FtZSBjaGFydA0KcGxvdChkZW5zaXR5KG9sZC52cy5uZXcucG9ydGZvbGlvJG9sZC5wb3J0Zm9saW8ucnRuKSkNCmxpbmVzKGRlbnNpdHkob2xkLnZzLm5ldy5wb3J0Zm9saW8kbmV3LnBvcnRmb2xpby5ydG4pLCBjb2wgPSAicmVkIikNCmBgYA0KVGhlIG5ldyBwb3J0Zm9saW8gc2VlbXMgdG8gaGF2ZSBsZXNzIHZhcmlhdGlvbiBiYXNlZCBvbiB0aGUgZGVuc2l0eSBsaW5lcy4NCg0KIyMjIEEgbW9yZSBhY2N1cmF0ZSBjb21wYXJpc29uIG9mIHBvcnRmb2xpb3MNCg0KTG9va2luZyBhdCB0aGUgdmFsdWUgYW5kIGRpc3RyaWJ1dGlvbiBvZiByZXR1cm5zIG9mIHlvdXIgcG9ydGZvbGlvIGlzIGEgZ29vZCBzdGFydCwgYnV0IGl0IGRvZXNuJ3QgbmVjZXNzYXJpbHkgdGVsbCB0aGUgd2hvbGUgc3RvcnkuIFlvdSBjb3VsZCBvYnZpb3VzbHkgbG9vayBhdCBtYW55IG90aGVyIGNoYXJ0cyBhbmQgbWV0cmljcywgYnV0IHVsdGltYXRlbHkgd2hhdCBtYXR0ZXJzIGlzIHBlcmZvcm1hbmNlLCBhbmQgc3BlY2lmaWNhbGx5IHBlcmlvZHMgb2YgcG9vciBwZXJmb3JtYW5jZS4NCg0KVGhlIFBlcmZvcm1hbmNlQW5hbHl0aWNzIHBhY2thZ2UgcHJvdmlkZXMgYWRkaXRpb25hbCB0b29scyB0byBnZXQgYSBmaW5lciB2aWV3IG9mIHlvdXIgcG9ydGZvbGlvLiBJbiBwYXJ0aWN1bGFyLCB0aGUgY2hhcnRzLlBlcmZvcm1hbmNlU3VtbWFyeSgpIGZ1bmN0aW9uIHByb3ZpZGVzIGEgcXVpY2sgYW5kIGVhc3kgd2F5IHRvIGRpc3BsYXkgdGhlIHBvcnRmb2xpbyB2YWx1ZSwgcmV0dXJucywgYW5kIHBlcmlvZHMgb2YgcG9vciBwZXJmb3JtYW5jZSwgYWxzbyBrbm93biBhcyBkcmF3ZG93bnMuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIHVzZSB0aGlzIG5ldyBmdW5jdGlvbiBvbiB0aGUgc2FtZSBvbGQgYW5kIG5ldyBwb3J0Zm9saW8gZGF0YSBpbiBvbGQudnMubmV3LnBvcnRmb2xpbyBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZS4NCmBgYHtyfQ0KIyBEcmF3IHZhbHVlLCByZXR1cm4sIGRyYXdkb3ducyBvZiBvbGQgcG9ydGZvbGlvDQpjaGFydHMuUGVyZm9ybWFuY2VTdW1tYXJ5KG9sZC52cy5uZXcucG9ydGZvbGlvJG9sZC5wb3J0Zm9saW8ucnRuKQ0KDQojIERyYXcgdmFsdWUsIHJldHVybiwgZHJhd2Rvd25zIG9mIG5ldyBwb3J0Zm9saW8NCmNoYXJ0cy5QZXJmb3JtYW5jZVN1bW1hcnkob2xkLnZzLm5ldy5wb3J0Zm9saW8kbmV3LnBvcnRmb2xpby5ydG4pDQoNCiMgRHJhdyBib3RoIHBvcnRmb2xpb3Mgb24gc2FtZSBjaGFydA0KY2hhcnRzLlBlcmZvcm1hbmNlU3VtbWFyeShvbGQudnMubmV3LnBvcnRmb2xpb1ssIGMoMywgNCldKQ0KYGBgDQpUaGUgbmV3IHBvcnRmb2xpbyBsb29rcyB0byBoYXZlIGEgaGlnaGVyIGN1bXVsYXRpdmUgcmV0dXJuIGFuZCBsb3dlciBkcmF3ZG93biBmb3IgdGhpcyBwZXJpb2Qgb2YgdGltZS4NCg0Kd2hhdCBncm91bmRzIHNob3VsZCB5b3UgYWRkIGEgbmV3IHN0b2NrIHRvIHlvdXIgcG9ydGZvbGlvPw0KDQpDb3JyZWxhdGlvbiB0byB5b3VyIGV4aXN0aW5nIHBvcnRmb2xpbyB0byBhc3Nlc3MgZGl2ZXJzaWZpY2F0aW9uLCByZXR1cm4gaGlzdG9ncmFtIHRvIGFzc2VzcyByaXNrIGFuZCBib3ggYW5kIHdoaXNrZXIgcGxvdCB0byBhc3Nlc3MgYXZlcmFnZSByZXR1cm4NCg0K