Prerequisites

For this course, you will need to download R and RStudio. One way to get R is to go to CRAN (the Comprehensive R archive Network).

RStudio is the IDE (integrated development environment) we’ll be using. You can download it from http://www.rstudio.com/download.

We will also use the patchwork package to easily arrange many plots into one single figure.

What is the tidyverse?

“The tidyverse is an opinionated collection of R packages designed for data science. All packages share an underlying design philosophy, grammar, and data structures.”

These packages help you import, tidy and understand data, to finally be able to communicate your findings in an easy way.

You can install the core packages from the tidyverse by simply typing install.packages("tidyverse") in the console.

# You only need to install a package once per device
install.packages("tidyverse")

The core tidyverse packages (ggplot2, tibble, tidyr, readr, dplyr, stringr, forcats, purrr) can be loaded then with the library() function.

There are many other packages that comply with the tidyverse data structure and practices that have to be installed and loaded independently. Some of those that we’ll be using throughout the course are tidymodels and a tidy ecosystem called tidyverts, specifically designed to provide tidy tools for time series analysis, which comprises of three main packages: tsibble, feasts and fable.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages --------------------------------------- tidyverse 1.3.0 --
v ggplot2 3.3.0     v purrr   0.3.4
v tibble  3.0.1     v dplyr   0.8.5
v tidyr   1.1.0     v stringr 1.4.0
v readr   1.3.1     v forcats 0.5.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(lubridate)

Attaching package: 㤼㸱lubridate㤼㸲

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

    intersect, setdiff, union

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

    date, intersect, setdiff, union
library(tsibble)

Attaching package: 㤼㸱tsibble㤼㸲

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

    interval

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

    id
library(feasts)
Loading required package: fabletools
library(fable)
library(patchwork)
library(tsibbledata)
library(nycflights13)

So, how does each of these packages help us tackle our data science problems?

Data science workflow

We can say that a data science workflow (aimed at forecasting) comprises the following general steps:

1. Import data

Data can be imported into R in many ways. The easiest way is importing files, such as .csv, .txt or MS Excel files. However, you can also import files from other statistical softwares or even connect RStudio to a database, such as SQL.

File importing

We have available different tidyverse-friendly packages to import specific files in your device. Usually, the object which the data gets imported to is a tibble.

Tibbles are the new data.frame, that have many advantages over their predecessor, such as a convenient print() method that doesn’t fill up your console, they don’t change variable types or names, among other things. You can learn more about tibbles here.

  • The readr package has many functions, like read_csv() or read_delim(), which allow us to import .csv, .txt, tab separated values, etc. It has functions to export data as well (write_csv(), for example).

  • If your data is stored in Excel files (.xls or .xlsx), you can use the functions from readxl.


* The haven package lets you load data from other statistical packages, such as SPSS, Stata and SAS.

Database connection

As said before, you can also connect your RStudio directly to a database. It’s straightforward to do so directly in RStudio’s IDE, just follow these simple steps:

  1. Go to the “Connections” tab and click on “New Connection”.

Connections tab

  1. A pop-up window will appear, prompting you to select your desired source.

Select the Data Source

  1. Follow the directions on screen and you’re good to go. You can always click the “Help” button for assistance. If everything went ok, you should be able to see all the tables contained in that DB.

Connecting to an SQL DB


2. Tidy data

All the tidyverse packages rely on having tidy data to work with. But, how can we know if our data is truly tidy? As pointed here, we need to know what values, variables and observations are and have them arranged the right way.

  • Values can be quantitative or qualitative.

  • A variable is a collection of values that measure the same attribute (sales, price, temperature, time).

  • An observation contains all the values measured from all variables in a single unit (a country, a day, a person, a company).

So, now, to consider our data as tidy (Wickham 2014), we must ensure that:

  1. Every column is one variable (and only one).

  2. Every row is just one observation.

  3. Every cell is a single value.

The tidyr package can help us achieve this by:

  • reshaping data by pivotting (pivot_longer() or pivot_wider()),

  • rectangling, which can handle nested data (such as JSON files) into tidy tibbles (unnest_longer() or unnest_wider()),

  • filling missing values, by replacing them (replace_na()), dropping such observations (drop_na()), or filling with the previous or next values (fill()),

  • splitting and combining character columns (separate() and unite()), among many other things.

The tsibble package is the foundation for the tidyverts. It provides a data infrastructure (a tsibble), which is data- and model-oriented object, where you have:

  • An index, which is the temporal variable that sets the ordering mechanism.

  • A key, that is a set of variables that define observational units over time.

This way, every observation is uniquely identified by index and key. Also, if the time series is regularly spaced, every observational unit should be measured with a common interval.

3. Understand your data

In order to properly understand our data, we might need to transform it, visualize it, and then model it.

Data transformation

The dplyr package contains functions specifically designed to help you transform tidy data. You can add new variables (mutate()), choose specific variables (select()), pick observations by their values (filter()) or by their position or index in the table (slice()), sort observations (arrange).

You can also group your data (group sales by store: group_by(data, store)).

Whenever you want to manipulate character variables, the stringr package is a great way to deal with the matter. Conveniently, all the stringr functions start with str_. Some of the things you can do with it are:

  • Detect matches, get the index where the pattern is found, count occurences or locate the position of the pattern within the string (str_detect(), `str_which(), str_count(), str_locate()).

  • Subset strings, by extracting substrings from vectors (str_sub()), subsetting a tibble to return only the observations that have the pattern (str_subset()), extract the string pattern (str_extract())…

  • Manage lengths (str_length(), str_pad(), str_trunc(), str_trim()),

  • Mutate, join and split strings, (str_replace(), str_to_lower(), str_c()).

Categorical variables, or Factors, as they’re called in R can be manipulated with the forcats package.

You can have either ordered factors, or unordered factors, and some functions that would help you handle them would be:

  • fct_reorder(), using another variable to specify the new order.

  • fct_infreq(), to order ir according to the frequency of values.

  • fct_relevel(), to manually choose the order.

  • fct_lump(), to collapse the least values of a factor into “other” category.

Handling date-time variables in R can be challenging with base R. Fortunately, the lubridate package is here for you.

For example, if you import an Excel sheet with a date variable on it, it will be parsed as character. You need to convert it to a date (or date-time) variable, in order to make calculations, plots, or anything relevant with it.

There are some very intuitive parsing functions to help you out, such as ymd() for dates stored with the order year month date (it can handle many formats such as “YYYY-MM-DD”, “YYYY/MM/DD”, “YYYY MM DD”, etc.), dmy() for cases when the day comes first, followed by month and year (“DD-MM-YYYY”). Date-time objects can also be parsed with ymd_hms(), ymd_h(), dmy_hm(), hms() just to mention a few.


Proceed to Visualization

Visualization


Base R plots and graphs are very basic (sometimes even ugly):

plot(data = mpg, hwy ~ displ)

Luckily, the ggplot2 package can produce astonishing plots and figures conveniently. It relies on the “Grammar of Graphics”, where you:

  • provide the data,

  • specify how you want to map the variables to aesthetics,

  • what type of plot or graph you want to produce

  • any other customization you’d like,

and ggplot2 takes care of it.

ggplot(mpg, aes(displ, hwy)) + 
  geom_point()

So, to generally describe how a ggplot2 plot works is as follows:

  1. Start with a ggplot() object, where you specify the data to be used,

  2. supply the aesthetic mapping (with aes()),

  3. add on layers:

    • If you want a scatterplot, use geom_point(), histogram geom_hist(). Other common plots are geom_line(), geom_bar(), geom_boxplot().
    • define color scales, such as scale_color_brewer() or scale_color_distiller(),
    • faceting specifications facet_wrap() or facet_grid()
    • coordinate systems, such as coord_cartesian(), coord_flip()

Every element is separated with a plus sign (+):

ggplot(mpg, aes(displ, hwy, colour = class)) +
  geom_point() +
  facet_wrap(~manufacturer)

It’s important to mention that the aesthetics can be passed inside the ggplot() function, or within a graphic primitive. In the former, the aesthetics are the same for all the layers, whereas in the latter, the aesthetics passed to a specific layer only affect that layer.

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth()


ggplot(mpg, aes(displ, hwy,color = class)) +
  geom_point() +
  geom_smooth()

We could not cover all the different variants that can be achieved with ggplot2 here, even if we tried.


Continue to Exploratory Data Analysis

Exploratory Data Analysis

Visually inspecting your data can give you insight to their dynamics, patterns, and historic behavior. However, before going into the modelling phase of the analysis, we must perform the exploratory data analysis (EDA).(Wickham and Grolemund 2016)

EDA involves making hypothesis regarding your data, transform and visually inspect statistical properties of it.

Some of the most important things to note on the EDA process are:

  • What type of variation do each variable have?

  • What is the covariation between variables?

  • Are there outliers present in the data?

  • What type of distribution do the variables follow?

Some examples

For categorical variables (factors), we can use a bar chart:

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut)) +
  ggtitle("Count of Diamonds by cut quality")

For continuous variables, a histogram can be used:

ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat), binwidth = 0.5) +
  ggtitle("Histogram of carats")

If you want to analyze the histogram of multiple variables in one single plot, you can use geom_freqpoly() or use facetting:

ggplot(data = diamonds %>% filter(carat < 3), mapping = aes(x = carat, colour = cut)) +
  geom_freqpoly(binwidth = 0.1)

ggplot(data = diamonds %>% filter(carat < 3), mapping = aes(x = carat, fill = cut)) +
  geom_histogram(binwidth = 0.1) +
  facet_wrap(~ cut) +
  theme(legend.position = "none")

Choosing the binwidth of the histogram can tell different stories or can reveal different patterns:

g <- ggplot(data = diamonds %>% filter(carat < 3), mapping = aes(x = carat))
g0 <- g + geom_histogram(binwidth = 0.5) +
  ggtitle("Binwidth = 0.5")
g1 <- g + geom_histogram(binwidth = 0.1) +
  ggtitle("Binwidth = 0.1")
g2 <- g + geom_histogram(binwidth = 0.01) +
  ggtitle("Binwidth = 0.01")
g3 <- g0/g1/g2
g3 + plot_annotation(title = "Histograms varying the binwidth",
                     subtitle = "Different patterns can arise when selecting different binwidths")

Another option is to go with boxplots:

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))+ labs(x = "class", y = "hwy mpg")

Again, these are just some examples, but the list of goes on and on.

Regarding time series analysis, the feasts package (Feature Extraction and Statistics for Time Series) has many functions that makes it easy for us to get further insight into our time series, We can get many graphs from it, such as:

aus_production %>% gg_season(Beer)

aus_production %>% filter(year(Quarter) > 1991) %>% gg_lag(Beer)

aus_production %>%
  model(STL(Beer ~ season(window = "periodic"))) %>% 
components() %>% 
  autoplot()

We can also get statistics from this package:

aus_retail %>%
  features(Turnover, feat_stl)


Go to Model

Model

Whenever we try to model a variable or phenomenom, it is said that we are trying to get a simplified version of reality. In fact, it’s simpler than that. What we are really trying to do is understand the way a variable or set of variables change, while ignoring or eliminating external “noise”.

It is well known to most data scientists that you cannot use the same data for modelling and testing (or forecasting). That’s way many people split their data into a training and testing set. However, a true data scientist has to be even stricter on its use of data:

It is recommended that you split your data into three sets:

1. ~ 60% of your data goes to the training set. Here, you can visualize it, perform all the model fitting and tweaking you want, over and over again.

2. ~ 20% of your data should go to a query set. With this query set, you can compare models by hand and visualize the outcomes.

3. The ~ 20% remaining data would conform the test set. Once you’ve compared all your models with the training and/or query sets, you can test your final model. This test can only be performed ONCE. This ensures no bias is introduced in the model and it remains a true forecast.


We will go through many different models aimed to provide forecasts for different situations.

The family of models we will be studying throughout the course are the following:

  1. Time series linear models (TSLM).
  2. Decomposition models.
  3. Exponential smoothing (ETS).
  4. ARIMA.
  5. Dynamic regression models.
  6. Introduction to tidymodels.

There are many ways to train models in R. As we said before, we will be using primarily two packages for this:

  • Whitin the tidyverts, we will use the fable package, which has many univariate and multivariate time series forecasting models. The way to fit one or more models in the fable package is by specifying them in the model() function.(Hyndman and Athanasopoulos 2019)
fit <- aus_retail %>%
  filter(
    State %in% c("New South Wales", "Victoria"),
    Industry == "Department stores"
  ) %>% 
  model(
    ets = ETS(box_cox(Turnover, 0.3)),
    arima = ARIMA(log(Turnover)),
    snaive = SNAIVE(Turnover)
  )

|=============                          | 33% ~12 s remaining    
|===================                    | 50% ~8 s remaining     
|==========================             | 67% ~4 s remaining     
|=======================================|100% ~0 s remaining     
fit
# A mable: 2 x 5
# Key:     State, Industry [2]
#   State           Industry          ets          arima                   snaive 
#   <chr>           <chr>             <model>      <model>                 <model>
# 1 New South Wales Department stores <ETS(A,Ad,A~ <ARIMA(2,1,1)(2,1,1)[1~ <SNAIV~
# 2 Victoria        Department stores <ETS(A,A,A)> <ARIMA(2,1,1)(1,1,2)[1~ <SNAIV~

This produces a mable (a model table), where every cell is a fitted model.

Using the report() function, you can get a detailed view of a particular model.

fit %>% 
  filter(State == "Victoria") %>% 
  select(State,Industry,arima) %>% 
  report()
Series: Turnover 
Model: ARIMA(2,1,1)(1,1,2)[12] 
Transformation: log(.x) 

Coefficients:
          ar1      ar2      ma1    sar1     sma1    sma2
      -0.2700  -0.1007  -0.7738  0.2676  -0.8690  0.0254
s.e.   0.0612   0.0588   0.0410  0.2378   0.2423  0.1818

sigma^2 estimated as 0.002547:  log likelihood=668.31
AIC=-1322.62   AICc=-1322.36   BIC=-1294.21

You could use the tidy() function to retrieve each model’s terms, estimates and statistics in a tidy way.

tidy(fit)
fit %>%
  filter(State == "Victoria") %>% 
  select(State,Industry,arima) %>%
  gg_tsresiduals() + ggtitle("Residual Diagnostics for the ARIMA model fitted to Victoria")

With the glance() function we can get further information for every model fitted.

glance(fit)


Go to Forecasting

4. Forecasting

Once we have trained one or more models that have a proper fit to our training data, we can now go ahead and produce forecasts. Using fable is as simple as running the forecast() function to our mable. It’s also quite easy to get plots from it:

fcst <- fit %>% 
  forecast(h = "2 years") 
fcst
fcst %>% 
  autoplot(filter(aus_retail, year(Month) > 2010), level = NULL) + 
  ggtitle("Forecast comparison across models and time series")

fcst %>% 
  filter(.model =="ets") %>% 
  autoplot(filter(aus_retail, year(Month) > 2010)) + 
  ggtitle("Forecast (with prediction intervals) for the ETS model")

5. Communicate

The last step of the workflow is to communicate your findings. This can be done in many different ways, depending on your target audience:

  • If you want to present results to your company and produce a reproducible tool that can be use in production, you could make a Shiny app.

R Markdown documents (just as the one you’re reading right now) are a way to make data science reports that join code, outputs and narrative. Given that it’s based on markdown, you can write \(\LaTeX\) equations, and so on.

\[ \int_{0}^{\infty} e^{-s \cdot t} f(t) d t=\lim _{h \rightarrow \infty} \int_{0}^{h} e^{-s, t} f(t) d t \]

  • If you want to publish your findings for the scientific community, you could write a paper.

  • You could also publish it online for anyone to access it.

Programming

In practice, you could automate the forecasting workflow by defining functions and iterations. The complete code that generates our analysis, from importing data, all the way to communicating your findings is a program.

The purrr package is an upgrade to R’s functional programming toolkit.

If you’re familiar with base R programming, you might have wondered about the %>% operator used in some of the examples above. %>% is called the pipe operator. It’s purpose is a tool to help you express a sequence of functions or operations in a more elegant and understandable way, and when reading code, you could translate %>% to “then”.

For example, if you have and Excel background, you might have had cases where your nested formulas where quite a challenge to understand in the long run, for example:

=SI.ERROR(INDICE(Costos[Costo],COINCIDIR("Supplies - Imaging "&[@Modelo],Costos[Group]&Costos[Model],0))*[@[Unidades de imagen totales]],"")

The same thing could happen in R. For example:

fit <- model(filter(aus_retail,State %in% c("New South Wales", "Victoria"), Industry == "Department stores"),ets = ETS(box_cox(Turnover, 0.3)), arima = ARIMA(log(Turnover)), snaive = SNAIVE(Turnover))
fit

It becomes very complicated to understand what the code is doing and in which order. As a matter of fact, this gives exactly the same result as the code we used in the Model section.

fit <- aus_retail %>%
  filter(
    State %in% c("New South Wales", "Victoria"),
    Industry == "Department stores"
  ) %>% 
  model(
    ets = ETS(box_cox(Turnover, 0.3)),
    arima = ARIMA(log(Turnover)),
    snaive = SNAIVE(Turnover)
  )

Reading it with the pipe operator, would be like:

  1. Define a variable called “fit”.

  2. Take the “aus_retail” data; then

  3. filter it, so that we keep only the states “New South Wales” and “Victoria” and the industry is “Department stores”; then

  4. Fit the three models: ETS, ARIMA and Seasonal Naïve.

It is not the purpose of this course to go in-depth in functional programming or learning to do tidy iterations, but we may do some examples along the way.






Examples

Let’s put in practice what we’ve seen so far with some examples.

A) Forecasting Population

We are tasked with producing forecasts of the rural population as a percentage in Mexico, Brazil and Argentina. We were provided with a dataset downloaded from the World Bank. Let’s import the world_bank_pop.csv file using readr:

1. Import data

pop <- read_csv("./data/world_bank_pop.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  country = col_character(),
  indicator = col_character()
)
See spec(...) for full column specifications.
# If you don't have the csv file, you can run:
# data(world_bank_pop, package = "tidyr")
# pop <- world_bank_pop
pop
# A tibble: 1,056 x 20
#    country indicator `2000` `2001` `2002` `2003`  `2004`  `2005`   `2006`   `2007`
#    <chr>   <chr>      <dbl>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>    <dbl>    <dbl>
#  1 ABW     SP.URB.T~ 4.24e4 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4  4.49e+4  4.47e+4
#  2 ABW     SP.URB.G~ 1.18e0 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2 -4.35e-1
#  3 ABW     SP.POP.T~ 9.09e4 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5  1.01e+5  1.01e+5
#  4 ABW     SP.POP.G~ 2.06e0 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0  7.98e-1  3.84e-1
#  5 AFG     SP.URB.T~ 4.44e6 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6  5.93e+6  6.15e+6
#  6 AFG     SP.URB.G~ 3.91e0 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0  4.12e+0  3.65e+0
#  7 AFG     SP.POP.T~ 2.01e7 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7  2.59e+7  2.66e+7
#  8 AFG     SP.POP.G~ 3.49e0 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0  3.23e+0  2.76e+0
#  9 AGO     SP.URB.T~ 8.23e6 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7  1.15e+7  1.21e+7
# 10 AGO     SP.URB.G~ 5.44e0 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0  4.92e+0  4.89e+0
# ... with 1,046 more rows, and 10 more variables: `2008` <dbl>, `2009` <dbl>,
#   `2010` <dbl>, `2011` <dbl>, `2012` <dbl>, `2013` <dbl>, `2014` <dbl>,
#   `2015` <dbl>, `2016` <dbl>, `2017` <dbl>

2. Tidy data

We can immediately see that our data isn’t tidy:

  1. Columns do not represent variables (we need to put the years in rows, not columns with pivot_longer).

  2. We must put variables in columns (column indicator) with pivot_wider().

  3. We can discard the variables that won’t be needed with select() in conjunction with contains().

  4. We will rename the variables with rename().

  5. We only have total population and urban population and the variables “country” and “year” are character. We must calculate the rural population as a percentage of the total, with mutate(). Here, we will also convert the “country” variable to factor using as_factor() and the “year” variable to double using as.integer().

  6. There are many countries that aren’t needed, so we need to keep only the ones of interest with filter().

  7. The data is a tibble object, we need to convert it to a tsibble using as_tsibble().

We can do this all in one step using the %>% (pipe) operator.

pop_tidy <- pop %>% 
  # i)
  pivot_longer(cols = -c(country, indicator),
               names_to = "year", values_to = "value") %>%
  # ii)
  pivot_wider(names_from = indicator,
              values_from = value) %>% 
  # iii)
  select(country,year,contains("TOTL")) %>% 
  # iv)
  rename(urban_pop = SP.URB.TOTL,total_pop = SP.POP.TOTL) %>% 
  #v)
  mutate(rural_pop_pct = (1 - urban_pop / total_pop)*100,
         country = as_factor(country),
         year = as.integer(year)
         ) %>% 
  # vi)
  filter(country %in% c("MEX","BRA","ARG")) %>% 
  #vii)
  as_tsibble(key = country, index = year)

pop_tidy
# A tsibble: 54 x 5 [1Y]
# Key:       country [3]
#    country  year urban_pop total_pop rural_pop_pct
#    <fct>   <int>     <dbl>     <dbl>         <dbl>
#  1 BRA      2000 142319498 175287587          18.8
#  2 BRA      2001 144961004 177750670          18.4
#  3 BRA      2002 147507656 180151021          18.1
#  4 BRA      2003 150005801 182482149          17.8
#  5 BRA      2004 152448023 184738458          17.5
#  6 BRA      2005 154831127 186917361          17.2
#  7 BRA      2006 157150590 189012412          16.9
#  8 BRA      2007 159407908 191026637          16.6
#  9 BRA      2008 161618007 192979029          16.3
# 10 BRA      2009 163798391 194895996          16.0
# ... with 44 more rows

3. Understanding the data

With our tidy data, we can proceed with the visualization and EDA. The end result will be the selection of one or more forecasting models.

The feasts package provides us with a convenient function autoplot() that detects the structure of the tsibble and applies a ggplot object to the data.

We split our data

pop_train <- pop_tidy %>% 
  filter(year <= 2009)
pop_query <- pop_tidy %>% 
  filter(year > 2009 & year <= 2013)
pop_train_query <- pop_tidy %>% 
  filter(year <= 2013)
# Total population plot
pop_train %>% 
  autoplot(total_pop) + ggtitle("Total population") + 
  ylab("")

# Rural population
pop_train %>%
  autoplot(rural_pop_pct) + ggtitle("Rural population (%)") + 
  ylab("")

The total population has an upward trend for Mexico and Brazil, and it seems fairly constant for Russia.

The rural population seems to be decreasing in all cases, with a steeper slope in both american countries.

We will fit three models to each time series:

  • A random walk model with drift.
  • A simple TSLM.
  • An ETS.
pop_fit <- pop_train %>% 
  model(`RW w/ drift` = RW(rural_pop_pct ~ drift()),
        `TSLM w/ trend` = TSLM(rural_pop_pct ~ trend()),
        ETS = ETS(rural_pop_pct ~ error("A") + trend("A") + season("N") )
        )
tidy(pop_fit)

4. Forecasting

pop_fcst <- pop_fit %>% 
  forecast(h = "4 years") 
pop_fcst %>% 
  autoplot(pop_train_query) +
  facet_grid(cols = vars(.model), rows = vars(country), scales = "free_y") + 
  guides(color = FALSE) +
  ylab("Rural population (%)")

We see that the models over estimate the decrease in rural population. We change the ETS model slightly and check if we get better results.

pop_fit2 <- pop_train %>% 
  model(`RW w/ drift` = RW(rural_pop_pct ~ drift()),
        `TSLM w/ trend` = TSLM(rural_pop_pct ~ trend()),
        ETS = ETS(rural_pop_pct ~ error("A") + trend("Ad") + season("N") )
        )
pop_fcst2 <- pop_fit2 %>% 
  forecast(h = "4 years") 

pop_fcst2 %>% 
  autoplot(pop_train_query) +
  facet_grid(cols = vars(.model), rows = vars(country), scales = "free_y") + 
  guides(color = FALSE) +
  ylab("Rural population (%)")

accuracy(pop_fcst2,pop_train_query) %>% 
  arrange(country, MAPE)

It seems now that we have a better accuracy on our forecast, especially with the ETS model. We will proceed then and make our complete forecast with this ETS model.

pop_train %>% 
  model(ETS = ETS(rural_pop_pct ~ error("A") + trend("Ad") + season("N") )
        ) %>% 
  forecast(h = "12 years") %>% 
  autoplot(pop_tidy) + 
  geom_vline(xintercept = 2014, linetype ="dashed", color = "red") +
  ylab("Rural population (%)") 

B) 2013 NYC Flights

The nycflights13 package contains all the flights that departed from one of NYC’s airports on 2013.

It contains several tibbles:

  • airlines

  • airports

  • flights

  • planes

  • weather

We can get a sneak peak at them by calling them.

airlines
airports
flights
planes
weather

References

Hyndman, Rob J, and George Athanasopoulos. 2019. Forecasting: Principles and Practice. 3rd ed. OTexts. https://otexts.com/fpp3/.

Wickham, Hadley. 2014. “Tidy Data.” The Journal of Statistical Software 59 (10). http://www.jstatsoft.org/v59/i10/.

Wickham, Hadley, and Garrett Grolemund. 2016. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. " O’Reilly Media, Inc.". https://r4ds.had.co.nz/.

LS0tDQp0aXRsZTogIlIgJiAqKmB0aWR5dmVyc2VgKiogLSAxMDEiDQpzdWJ0aXRsZTogIlRpbWUgU2VyaWVzIEFuYWx5c2lzIg0KYXV0aG9yOiAiUGFibG8gQmVuYXZpZGVzLUhlcnJlcmEiDQpkYXRlOiAyMDIwLTA1LTIwDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIHRoZW1lOiBkYXJrbHkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQpiaWJsaW9ncmFwaHk6IHN0X3JlZmVyZW5jZXMuYmliDQotLS0NCg0KYGBge2NzcyBDU1Mgc3R5bGVzLCBlY2hvPUZBTFNFfQ0KY29kZSB7DQogIGJhY2tncm91bmQtY29sb3I6IHJnYmEoMTA1LCAxMDUsIDEwNSwwLjUpOw0KfQ0KaDEgew0KICBjb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50DQp9DQpoMiB7DQogIGNvbG9yOiBkZWVwc2t5Ymx1ZSAhaW1wb3J0YW50DQp9DQpoMyB7DQogIGNvbG9yOiB0b21hdG8gIWltcG9ydGFudA0KfQ0KYGBgDQoNCg0KDQoNCg0KIVtdKGltYWdlcy9yX3RpZHl2ZXJzZS5qcGcpDQoNCiMgUHJlcmVxdWlzaXRlcw0KDQpGb3IgdGhpcyBjb3Vyc2UsIHlvdSB3aWxsIG5lZWQgdG8gZG93bmxvYWQgUiBhbmQgUlN0dWRpby4gT25lIHdheSB0byBnZXQgUiBpcyB0byBnbyB0byBbQ1JBTl0oaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnLykgKHRoZSAqKkMqKm9tcHJlaGVuc2l2ZSAqKlIqKiBhcmNoaXZlICoqTioqZXR3b3JrKS4NCg0KIVtdKGltYWdlcy9SU3R1ZGlvLnBuZyl7d2lkdGg9MTUlfQ0KDQpSU3R1ZGlvIGlzIHRoZSBJREUgKGludGVncmF0ZWQgZGV2ZWxvcG1lbnQgZW52aXJvbm1lbnQpIHdlJ2xsIGJlIHVzaW5nLiBZb3UgY2FuIGRvd25sb2FkIGl0IGZyb20gaHR0cDovL3d3dy5yc3R1ZGlvLmNvbS9kb3dubG9hZC4gDQoNCldlIHdpbGwgYWxzbyB1c2UgdGhlIGBwYXRjaHdvcmtgIHBhY2thZ2UgdG8gZWFzaWx5IGFycmFuZ2UgbWFueSBwbG90cyBpbnRvIG9uZSBzaW5nbGUgZmlndXJlLg0KDQojIFdoYXQgaXMgdGhlIHRpZHl2ZXJzZT8geyN0aWR5dmVyc2V9DQoNCiJUaGUgW2B0aWR5dmVyc2VgXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgaXMgYW4gb3BpbmlvbmF0ZWQgY29sbGVjdGlvbiBvZiBSIHBhY2thZ2VzIGRlc2lnbmVkIGZvciBkYXRhIHNjaWVuY2UuIEFsbCBwYWNrYWdlcyBzaGFyZSBhbiB1bmRlcmx5aW5nIGRlc2lnbiBwaGlsb3NvcGh5LCBncmFtbWFyLCBhbmQgZGF0YSBzdHJ1Y3R1cmVzLiINCg0KVGhlc2UgcGFja2FnZXMgaGVscCB5b3UgaW1wb3J0LCB0aWR5IGFuZCB1bmRlcnN0YW5kIGRhdGEsIHRvIGZpbmFsbHkgYmUgYWJsZSB0byBjb21tdW5pY2F0ZSB5b3VyIGZpbmRpbmdzIGluIGFuIGVhc3kgd2F5Lg0KDQpZb3UgY2FuIGluc3RhbGwgdGhlIGNvcmUgcGFja2FnZXMgZnJvbSB0aGUgYHRpZHl2ZXJzZWAgYnkgc2ltcGx5IHR5cGluZyBgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIilgIGluIHRoZSBjb25zb2xlLg0KDQpgYGB7ciBpbnN0YWxsIHRpZHl2ZXJzZSwgZXZhbD1GQUxTRX0NCiMgWW91IG9ubHkgbmVlZCB0byBpbnN0YWxsIGEgcGFja2FnZSBvbmNlIHBlciBkZXZpY2UNCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpgYGANCg0KVGhlIGNvcmUgYHRpZHl2ZXJzZWAgcGFja2FnZXMgKGBnZ3Bsb3QyYCwgYHRpYmJsZWAsIGB0aWR5cmAsIGByZWFkcmAsIGBkcGx5cmAsIGBzdHJpbmdyYCwgYGZvcmNhdHNgLCBgcHVycnJgKSBjYW4gYmUgbG9hZGVkIHRoZW4gd2l0aCB0aGUgYGxpYnJhcnkoKWAgZnVuY3Rpb24uDQoNClRoZXJlIGFyZSBtYW55IG90aGVyIHBhY2thZ2VzIHRoYXQgY29tcGx5IHdpdGggdGhlIGB0aWR5dmVyc2VgIGRhdGEgc3RydWN0dXJlIGFuZCBwcmFjdGljZXMgdGhhdCBoYXZlIHRvIGJlIGluc3RhbGxlZCBhbmQgbG9hZGVkIGluZGVwZW5kZW50bHkuIFNvbWUgb2YgdGhvc2UgdGhhdCB3ZSdsbCBiZSB1c2luZyB0aHJvdWdob3V0IHRoZSBjb3Vyc2UgYXJlIGB0aWR5bW9kZWxzYCBhbmQgYSB0aWR5IGVjb3N5c3RlbSBjYWxsZWQgW2B0aWR5dmVydHNgXShodHRwczovL3RpZHl2ZXJ0cy5vcmcvKSwgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIHRvIHByb3ZpZGUgdGlkeSB0b29scyBmb3IgdGltZSBzZXJpZXMgYW5hbHlzaXMsIHdoaWNoIGNvbXByaXNlcyBvZiB0aHJlZSBtYWluIHBhY2thZ2VzOiBgdHNpYmJsZWAsIGBmZWFzdHNgIGFuZCBgZmFibGVgLg0KDQpgYGB7ciBwa2dzfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkodHNpYmJsZSkNCmxpYnJhcnkoZmVhc3RzKQ0KbGlicmFyeShmYWJsZSkNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KbGlicmFyeSh0c2liYmxlZGF0YSkNCmxpYnJhcnkobnljZmxpZ2h0czEzKQ0KYGBgDQoNCg0KDQohW10oaW1hZ2VzL3RpZHl2ZXJzZS5wbmcpDQpTbywgaG93IGRvZXMgZWFjaCBvZiB0aGVzZSBwYWNrYWdlcyBoZWxwIHVzIHRhY2tsZSBvdXIgZGF0YSBzY2llbmNlIHByb2JsZW1zPw0KDQojIERhdGEgc2NpZW5jZSB3b3JrZmxvdyB7I3dvcmtmbG93IC50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQpXZSBjYW4gc2F5IHRoYXQgYSBkYXRhIHNjaWVuY2Ugd29ya2Zsb3cgKGFpbWVkIGF0IGZvcmVjYXN0aW5nKSBjb21wcmlzZXMgdGhlIGZvbGxvd2luZyBnZW5lcmFsIHN0ZXBzOg0KDQojIyAxLiBJbXBvcnQgZGF0YSB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpEYXRhIGNhbiBiZSBpbXBvcnRlZCBpbnRvIFIgaW4gbWFueSB3YXlzLiBUaGUgZWFzaWVzdCB3YXkgaXMgaW1wb3J0aW5nIGZpbGVzLCBzdWNoIGFzIC5jc3YsIC50eHQgb3IgTVMgRXhjZWwgZmlsZXMuIEhvd2V2ZXIsIHlvdSBjYW4gYWxzbyBpbXBvcnQgZmlsZXMgZnJvbSBvdGhlciBzdGF0aXN0aWNhbCBzb2Z0d2FyZXMgb3IgZXZlbiBjb25uZWN0IFJTdHVkaW8gdG8gYSBkYXRhYmFzZSwgc3VjaCBhcyBTUUwuDQoNCiMjIyBGaWxlIGltcG9ydGluZw0KDQpXZSBoYXZlIGF2YWlsYWJsZSBkaWZmZXJlbnQgdGlkeXZlcnNlLWZyaWVuZGx5IHBhY2thZ2VzIHRvIGltcG9ydCBzcGVjaWZpYyBmaWxlcyBpbiB5b3VyIGRldmljZS4gVXN1YWxseSwgdGhlIG9iamVjdCB3aGljaCB0aGUgZGF0YSBnZXRzIGltcG9ydGVkIHRvIGlzIGEgW2B0aWJibGVgXShodHRwczovL3RpYmJsZS50aWR5dmVyc2Uub3JnLykuDQoNCiFbXShpbWFnZXMvdGliYmxlLnBuZyl7d2lkdGg9MTUlfQ0KDQpgVGliYmxlc2AgYXJlIHRoZSBuZXcgYGRhdGEuZnJhbWVgLCB0aGF0IGhhdmUgbWFueSBhZHZhbnRhZ2VzIG92ZXIgdGhlaXIgcHJlZGVjZXNzb3IsIHN1Y2ggYXMgYSBjb252ZW5pZW50IGBwcmludCgpYCBtZXRob2QgdGhhdCBkb2Vzbid0IGZpbGwgdXAgeW91ciBjb25zb2xlLCB0aGV5IGRvbid0IGNoYW5nZSB2YXJpYWJsZSB0eXBlcyBvciBuYW1lcywgYW1vbmcgb3RoZXIgdGhpbmdzLiBZb3UgY2FuIGxlYXJuIG1vcmUgYWJvdXQgdGliYmxlcyBbaGVyZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWJibGVzLmh0bWwpLg0KDQohW10oaW1hZ2VzL3JlYWRyLnBuZyl7d2lkdGg9MTUlfQ0KDQogICogVGhlIFtgcmVhZHJgXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGhhcyBtYW55IGZ1bmN0aW9ucywgbGlrZSBgcmVhZF9jc3YoKWAgb3IgYHJlYWRfZGVsaW0oKWAsIHdoaWNoIGFsbG93IHVzIHRvIGltcG9ydCAuY3N2LCAudHh0LCB0YWIgc2VwYXJhdGVkIHZhbHVlcywgZXRjLiBJdCBoYXMgZnVuY3Rpb25zIHRvIGV4cG9ydCBkYXRhIGFzIHdlbGwgKGB3cml0ZV9jc3YoKWAsIGZvciBleGFtcGxlKS4NCg0KIVtdKGltYWdlcy9yZWFkeGwucG5nKXt3aWR0aD0xNSV9DQogICAgDQogICogSWYgeW91ciBkYXRhIGlzIHN0b3JlZCBpbiBFeGNlbCBmaWxlcyAoLnhscyBvciAueGxzeCksIHlvdSBjYW4gdXNlIHRoZSBmdW5jdGlvbnMgZnJvbSBbYHJlYWR4bGBdKGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcvKS4NCg0KIVtdKGltYWdlcy9oYXZlbi5wbmcpe3dpZHRoPTE1JX0gICAgDQogICogVGhlIFtgaGF2ZW5gXShodHRwczovL2hhdmVuLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGxldHMgeW91IGxvYWQgZGF0YSBmcm9tIG90aGVyIHN0YXRpc3RpY2FsIHBhY2thZ2VzLCBzdWNoIGFzIFNQU1MsIFN0YXRhIGFuZCBTQVMuDQogIA0KIyMjIERhdGFiYXNlIGNvbm5lY3Rpb24NCg0KQXMgc2FpZCBiZWZvcmUsIHlvdSBjYW4gYWxzbyBjb25uZWN0IHlvdXIgUlN0dWRpbyBkaXJlY3RseSB0byBhIGRhdGFiYXNlLiBJdCdzIHN0cmFpZ2h0Zm9yd2FyZCB0byBkbyBzbyBkaXJlY3RseSBpbiBSU3R1ZGlvJ3MgSURFLCBqdXN0IGZvbGxvdyB0aGVzZSBzaW1wbGUgc3RlcHM6DQoNCjEuIEdvIHRvIHRoZSAiKipDb25uZWN0aW9ucyoqIiB0YWIgYW5kIGNsaWNrIG9uICoqIk5ldyBDb25uZWN0aW9uIioqLg0KDQohWypDb25uZWN0aW9ucyB0YWIqXShpbWFnZXMvY29ubmVjdGlvbnMuUE5HKXt3aWR0aD05MCV9DQoNCjIuIEEgcG9wLXVwIHdpbmRvdyB3aWxsIGFwcGVhciwgcHJvbXB0aW5nIHlvdSB0byBzZWxlY3QgeW91ciBkZXNpcmVkIHNvdXJjZS4NCg0KIVsqU2VsZWN0IHRoZSBEYXRhIFNvdXJjZSpdKGltYWdlcy9jb25uZWN0aW9ucyBwb3B1cC5QTkcpe3dpZHRoPTYwJX0NCg0KMy4gRm9sbG93IHRoZSBkaXJlY3Rpb25zIG9uIHNjcmVlbiBhbmQgeW91J3JlIGdvb2QgdG8gZ28uIFlvdSBjYW4gYWx3YXlzIGNsaWNrIHRoZSAiSGVscCIgYnV0dG9uIGZvciBhc3Npc3RhbmNlLiBJZiBldmVyeXRoaW5nIHdlbnQgb2ssIHlvdSBzaG91bGQgYmUgYWJsZSB0byBzZWUgYWxsIHRoZSB0YWJsZXMgY29udGFpbmVkIGluIHRoYXQgREIuDQoNCiFbKkNvbm5lY3RpbmcgdG8gYW4gU1FMIERCKl0oaW1hZ2VzL2Nvbm5lY3Rpb24gc3FsLlBORyl7d2lkdGg9NzAlfQ0KDQo8YnI+DQoNCiMjIDIuIFRpZHkgZGF0YSB7I3RpZHl9DQoNCkFsbCB0aGUgYHRpZHl2ZXJzZWAgcGFja2FnZXMgcmVseSBvbiBoYXZpbmcgdGlkeSBkYXRhIHRvIHdvcmsgd2l0aC4gQnV0LCBob3cgY2FuIHdlIGtub3cgaWYgb3VyIGRhdGEgaXMgdHJ1bHkgdGlkeT8gQXMgcG9pbnRlZCBbaGVyZV0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3RpZHktZGF0YS5odG1sKSwgd2UgbmVlZCB0byBrbm93IHdoYXQgKip2YWx1ZXMsIHZhcmlhYmxlcyBhbmQgb2JzZXJ2YXRpb25zKiogYXJlIGFuZCBoYXZlIHRoZW0gYXJyYW5nZWQgdGhlIHJpZ2h0IHdheS4NCiAgDQogICogKipWYWx1ZXMqKiBjYW4gYmUgcXVhbnRpdGF0aXZlIG9yIHF1YWxpdGF0aXZlLg0KICAgIA0KICAqIEEgKip2YXJpYWJsZSoqIGlzIGEgY29sbGVjdGlvbiBvZiAqdmFsdWVzKiB0aGF0IG1lYXN1cmUgdGhlIHNhbWUgYXR0cmlidXRlIChzYWxlcywgcHJpY2UsIHRlbXBlcmF0dXJlLCB0aW1lKS4NCiAgICANCiAgKiBBbiAqKm9ic2VydmF0aW9uKiogY29udGFpbnMgYWxsIHRoZSB2YWx1ZXMgbWVhc3VyZWQgZnJvbSBhbGwgdmFyaWFibGVzIGluIGEgc2luZ2xlIHVuaXQgKGEgY291bnRyeSwgYSBkYXksIGEgcGVyc29uLCBhIGNvbXBhbnkpLg0KICAgIA0KU28sIG5vdywgdG8gY29uc2lkZXIgb3VyIGRhdGEgYXMgKip0aWR5KiogW0BXaWNraGFtMjAxNF0sIHdlIG11c3QgZW5zdXJlIHRoYXQ6DQogICAgDQogIGkpIEV2ZXJ5IGNvbHVtbiBpcyBvbmUgdmFyaWFibGUgKGFuZCBvbmx5IG9uZSkuDQogICAgDQogIGlpKSBFdmVyeSByb3cgaXMganVzdCBvbmUgb2JzZXJ2YXRpb24uDQogICAgDQogIGlpaSkgRXZlcnkgY2VsbCBpcyBhIHNpbmdsZSB2YWx1ZS4NCg0KIVtdKGltYWdlcy90aWR5ci5wbmcpe3dpZHRoPTE1JX0NCg0KVGhlIFtgdGlkeXJgXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGNhbiBoZWxwIHVzIGFjaGlldmUgdGhpcyBieToNCg0KKiAgICoqcmVzaGFwaW5nKiogZGF0YSBieSBwaXZvdHRpbmcgKGBwaXZvdF9sb25nZXIoKWAgb3IgYHBpdm90X3dpZGVyKClgKSwNCg0KKiAgICoqcmVjdGFuZ2xpbmcqKiwgd2hpY2ggY2FuIGhhbmRsZSBuZXN0ZWQgZGF0YSAoc3VjaCBhcyBKU09OIGZpbGVzKSBpbnRvIHRpZHkgdGliYmxlcyAoYHVubmVzdF9sb25nZXIoKWAgb3IgYHVubmVzdF93aWRlcigpYCksIA0KDQoqICAgKipmaWxsaW5nIG1pc3NpbmcgdmFsdWVzKiosIGJ5IHJlcGxhY2luZyB0aGVtIChgcmVwbGFjZV9uYSgpYCksIGRyb3BwaW5nIHN1Y2ggb2JzZXJ2YXRpb25zIChgZHJvcF9uYSgpYCksIG9yIGZpbGxpbmcgd2l0aCB0aGUgcHJldmlvdXMgb3IgbmV4dCB2YWx1ZXMgKGBmaWxsKClgKSwNCg0KKiAqKnNwbGl0dGluZyBhbmQgY29tYmluaW5nIGNoYXJhY3RlciBjb2x1bW5zKiogKGBzZXBhcmF0ZSgpYCBhbmQgYHVuaXRlKClgKSwgYW1vbmcgbWFueSBvdGhlciB0aGluZ3MuDQoNCiFbXShpbWFnZXMvdHNpYmJsZS5QTkcpe3dpZHRoPTE1JX0NCg0KVGhlIFtgdHNpYmJsZWBdKGh0dHBzOi8vdHNpYmJsZS50aWR5dmVydHMub3JnLykgcGFja2FnZSBpcyB0aGUgZm91bmRhdGlvbiBmb3IgdGhlIGB0aWR5dmVydHNgLiBJdCBwcm92aWRlcyBhIGRhdGEgaW5mcmFzdHJ1Y3R1cmUgKGEgYHRzaWJibGVgKSwgd2hpY2ggaXMgZGF0YS0gYW5kIG1vZGVsLW9yaWVudGVkIG9iamVjdCwgd2hlcmUgeW91IGhhdmU6DQoNCiogQW4gKippbmRleCoqLCB3aGljaCBpcyB0aGUgdGVtcG9yYWwgdmFyaWFibGUgdGhhdCBzZXRzIHRoZSBvcmRlcmluZyBtZWNoYW5pc20uDQoNCiogQSAqKmtleSoqLCB0aGF0IGlzIGEgc2V0IG9mIHZhcmlhYmxlcyB0aGF0IGRlZmluZSBvYnNlcnZhdGlvbmFsIHVuaXRzIG92ZXIgdGltZS4NCg0KVGhpcyB3YXksIGV2ZXJ5IG9ic2VydmF0aW9uIGlzIHVuaXF1ZWx5IGlkZW50aWZpZWQgYnkgaW5kZXggYW5kIGtleS4gQWxzbywgaWYgdGhlIHRpbWUgc2VyaWVzIGlzIHJlZ3VsYXJseSBzcGFjZWQsIGV2ZXJ5IG9ic2VydmF0aW9uYWwgdW5pdCBzaG91bGQgYmUgbWVhc3VyZWQgd2l0aCBhIGNvbW1vbiAqKmludGVydmFsKiouDQoNCg0KDQoNCg0KIyMgMy4gVW5kZXJzdGFuZCB5b3VyIGRhdGEgeyN1bmRlcnN0YW5kIC50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KSW4gb3JkZXIgdG8gcHJvcGVybHkgdW5kZXJzdGFuZCBvdXIgZGF0YSwgd2UgbWlnaHQgbmVlZCB0byAqKnRyYW5zZm9ybSoqIGl0LCAqKnZpc3VhbGl6ZSoqIGl0LCBhbmQgdGhlbiAqKm1vZGVsKiogaXQuDQoNCg0KIyMjIERhdGEgdHJhbnNmb3JtYXRpb24geyNkYXRhX3RyYW5zZm9ybWF0aW9ufQ0KDQohW10oaW1hZ2VzL2RwbHlyLnBuZyl7d2lkdGg9MTUlfQ0KDQpUaGUgW2BkcGx5cmBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgY29udGFpbnMgZnVuY3Rpb25zIHNwZWNpZmljYWxseSBkZXNpZ25lZCB0byBoZWxwIHlvdSB0cmFuc2Zvcm0gdGlkeSBkYXRhLiBZb3UgY2FuIGFkZCBuZXcgdmFyaWFibGVzIChgbXV0YXRlKClgKSwgY2hvb3NlIHNwZWNpZmljIHZhcmlhYmxlcyAoYHNlbGVjdCgpYCksIHBpY2sgb2JzZXJ2YXRpb25zIGJ5IHRoZWlyIHZhbHVlcyAoYGZpbHRlcigpYCkgb3IgYnkgdGhlaXIgcG9zaXRpb24gb3IgaW5kZXggaW4gdGhlIHRhYmxlIChgc2xpY2UoKWApLCBzb3J0IG9ic2VydmF0aW9ucyAoYGFycmFuZ2VgKS4NCg0KWW91IGNhbiBhbHNvIGdyb3VwIHlvdXIgZGF0YSAoZ3JvdXAgc2FsZXMgYnkgc3RvcmU6IGBncm91cF9ieShkYXRhLCBzdG9yZSlgKS4NCg0KIVtdKGltYWdlcy9zdHJpbmdyLnBuZyl7d2lkdGg9MTUlfQ0KDQpXaGVuZXZlciB5b3Ugd2FudCB0byBtYW5pcHVsYXRlIGNoYXJhY3RlciB2YXJpYWJsZXMsIHRoZSBbYHN0cmluZ3JgXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgaXMgYSBncmVhdCB3YXkgdG8gZGVhbCB3aXRoIHRoZSBtYXR0ZXIuIENvbnZlbmllbnRseSwgYWxsIHRoZSBgc3RyaW5ncmAgZnVuY3Rpb25zIHN0YXJ0IHdpdGggYHN0cl9gLiBTb21lIG9mIHRoZSB0aGluZ3MgeW91IGNhbiBkbyB3aXRoIGl0IGFyZToNCg0KKiAqKkRldGVjdCBtYXRjaGVzKiosIGdldCB0aGUgaW5kZXggd2hlcmUgdGhlIHBhdHRlcm4gaXMgZm91bmQsIGNvdW50IG9jY3VyZW5jZXMgb3IgbG9jYXRlIHRoZSBwb3NpdGlvbiBvZiB0aGUgcGF0dGVybiB3aXRoaW4gdGhlIHN0cmluZyAoYHN0cl9kZXRlY3QoKWAsIGBgc3RyX3doaWNoKClgLCBgc3RyX2NvdW50KClgLCBgc3RyX2xvY2F0ZSgpYCkuDQoNCiogKipTdWJzZXQgc3RyaW5ncyoqLCBieSBleHRyYWN0aW5nIHN1YnN0cmluZ3MgZnJvbSB2ZWN0b3JzIChgc3RyX3N1YigpYCksIHN1YnNldHRpbmcgYSB0aWJibGUgdG8gcmV0dXJuIG9ubHkgdGhlIG9ic2VydmF0aW9ucyB0aGF0IGhhdmUgdGhlIHBhdHRlcm4gKGBzdHJfc3Vic2V0KClgKSwgZXh0cmFjdCB0aGUgc3RyaW5nIHBhdHRlcm4gKGBzdHJfZXh0cmFjdCgpYCkuLi4NCg0KKiAqKk1hbmFnZSBsZW5ndGhzKiogKGBzdHJfbGVuZ3RoKClgLCBgc3RyX3BhZCgpYCwgYHN0cl90cnVuYygpYCwgYHN0cl90cmltKClgKSwNCg0KKiAqKk11dGF0ZSwgam9pbiBhbmQgc3BsaXQqKiBzdHJpbmdzLCAoYHN0cl9yZXBsYWNlKClgLCBgc3RyX3RvX2xvd2VyKClgLCBgc3RyX2MoKWApLg0KDQohW10oaW1hZ2VzL2ZvcmNhdHMucG5nKXt3aWR0aD0xNSV9DQoNCkNhdGVnb3JpY2FsIHZhcmlhYmxlcywgb3IgKipGYWN0b3JzKiosIGFzIHRoZXkncmUgY2FsbGVkIGluICoqUioqIGNhbiBiZSBtYW5pcHVsYXRlZCB3aXRoIHRoZSBbYGZvcmNhdHNgXShodHRwczovL2ZvcmNhdHMudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UuDQoNCllvdSBjYW4gaGF2ZSBlaXRoZXIgb3JkZXJlZCBmYWN0b3JzLCBvciB1bm9yZGVyZWQgZmFjdG9ycywgYW5kIHNvbWUgZnVuY3Rpb25zIHRoYXQgd291bGQgaGVscCB5b3UgaGFuZGxlIHRoZW0gd291bGQgYmU6DQoNCiogYGZjdF9yZW9yZGVyKClgLCB1c2luZyBhbm90aGVyIHZhcmlhYmxlIHRvIHNwZWNpZnkgdGhlIG5ldyBvcmRlci4NCg0KKiBgZmN0X2luZnJlcSgpYCwgdG8gb3JkZXIgaXIgYWNjb3JkaW5nIHRvIHRoZSBmcmVxdWVuY3kgb2YgdmFsdWVzLg0KDQoqIGBmY3RfcmVsZXZlbCgpYCwgdG8gbWFudWFsbHkgY2hvb3NlIHRoZSBvcmRlci4NCg0KKiBgZmN0X2x1bXAoKWAsIHRvIGNvbGxhcHNlIHRoZSBsZWFzdCB2YWx1ZXMgb2YgYSBmYWN0b3IgaW50byAib3RoZXIiIGNhdGVnb3J5Lg0KDQoNCiFbXShpbWFnZXMvbHVicmlkYXRlLnBuZyl7d2lkdGg9MTUlfQ0KDQoNCkhhbmRsaW5nIGRhdGUtdGltZSB2YXJpYWJsZXMgaW4gUiBjYW4gYmUgY2hhbGxlbmdpbmcgd2l0aCBiYXNlIFIuIEZvcnR1bmF0ZWx5LCB0aGUgW2BsdWJyaWRhdGVgXShodHRwczovL2x1YnJpZGF0ZS50aWR5dmVyc2Uub3JnLykgcGFja2FnZSBpcyBoZXJlIGZvciB5b3UuDQoNCkZvciBleGFtcGxlLCBpZiB5b3UgaW1wb3J0IGFuIEV4Y2VsIHNoZWV0IHdpdGggYSBkYXRlIHZhcmlhYmxlIG9uIGl0LCBpdCB3aWxsIGJlIHBhcnNlZCBhcyBjaGFyYWN0ZXIuIFlvdSBuZWVkIHRvIGNvbnZlcnQgaXQgdG8gYSBkYXRlIChvciBkYXRlLXRpbWUpIHZhcmlhYmxlLCBpbiBvcmRlciB0byBtYWtlIGNhbGN1bGF0aW9ucywgcGxvdHMsIG9yIGFueXRoaW5nIHJlbGV2YW50IHdpdGggaXQuDQoNClRoZXJlIGFyZSBzb21lIHZlcnkgaW50dWl0aXZlIHBhcnNpbmcgZnVuY3Rpb25zIHRvIGhlbHAgeW91IG91dCwgc3VjaCBhcyBgeW1kKClgIGZvciBkYXRlcyBzdG9yZWQgd2l0aCB0aGUgb3JkZXIgeWVhciBtb250aCBkYXRlIChpdCBjYW4gaGFuZGxlIG1hbnkgZm9ybWF0cyBzdWNoIGFzICJZWVlZLU1NLUREIiwgIllZWVkvTU0vREQiLCAiWVlZWSBNTSBERCIsIGV0Yy4pLCBgZG15KClgIGZvciBjYXNlcyB3aGVuIHRoZSBkYXkgY29tZXMgZmlyc3QsIGZvbGxvd2VkIGJ5IG1vbnRoIGFuZCB5ZWFyICgiREQtTU0tWVlZWSIpLiBEYXRlLXRpbWUgb2JqZWN0cyBjYW4gYWxzbyBiZSBwYXJzZWQgd2l0aCBgeW1kX2htcygpYCwgYHltZF9oKClgLCBgZG15X2htKClgLCBgaG1zKClgIGp1c3QgdG8gbWVudGlvbiBhIGZldy4gDQoNCjxicj4NCg0KPGZvbnQgc2l6ZT0iNSI+IFByb2NlZWQgdG8gW1Zpc3VhbGl6YXRpb25dKCN1bmRlcnN0YW5kKSA8L2ZvbnQ+IA0KDQoNCiMjIyBWaXN1YWxpemF0aW9uIHsjdmlzdWFsaXphdGlvbn0NCg0KPGJyPg0KDQpCYXNlICoqUioqIHBsb3RzIGFuZCBncmFwaHMgYXJlIHZlcnkgYmFzaWMgKHNvbWV0aW1lcyBldmVuIHVnbHkpOg0KDQpgYGB7ciBiYXNlIHIgcGxvdCwgZWNobz1UUlVFfQ0KcGxvdChkYXRhID0gbXBnLCBod3kgfiBkaXNwbCkNCmBgYA0KDQohW10oaW1hZ2VzL2dncGxvdDIucG5nKXt3aWR0aD0xNSV9DQoNCkx1Y2tpbHksIHRoZSBbYGdncGxvdDJgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgY2FuIHByb2R1Y2UgYXN0b25pc2hpbmcgcGxvdHMgYW5kIGZpZ3VyZXMgY29udmVuaWVudGx5LiBJdCByZWxpZXMgb24gdGhlICJHcmFtbWFyIG9mIEdyYXBoaWNzIiwgd2hlcmUgeW91Og0KDQoqIHByb3ZpZGUgdGhlIGRhdGEsDQoNCiogc3BlY2lmeSBob3cgeW91IHdhbnQgdG8gbWFwIHRoZSB2YXJpYWJsZXMgdG8gYWVzdGhldGljcywNCg0KKiB3aGF0IHR5cGUgb2YgcGxvdCBvciBncmFwaCB5b3Ugd2FudCB0byBwcm9kdWNlDQoNCiogYW55IG90aGVyIGN1c3RvbWl6YXRpb24geW91J2QgbGlrZSwNCg0KYW5kIGBnZ3Bsb3QyYCB0YWtlcyBjYXJlIG9mIGl0Lg0KDQpgYGB7ciBiYXNpYyBnZ3Bsb3QsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KU28sIHRvIGdlbmVyYWxseSBkZXNjcmliZSBob3cgYSBgZ2dwbG90MmAgcGxvdCB3b3JrcyBpcyBhcyBmb2xsb3dzOg0KDQoxLiBTdGFydCB3aXRoIGEgYGdncGxvdCgpYCBvYmplY3QsIHdoZXJlIHlvdSBzcGVjaWZ5IHRoZSBkYXRhIHRvIGJlIHVzZWQsDQoNCjIuIHN1cHBseSB0aGUgYWVzdGhldGljIG1hcHBpbmcgKHdpdGggYGFlcygpYCksDQoNCjMuIGFkZCBvbiBsYXllcnM6DQogICAgKiBJZiB5b3Ugd2FudCBhIHNjYXR0ZXJwbG90LCB1c2UgYGdlb21fcG9pbnQoKWAsIGhpc3RvZ3JhbSBgZ2VvbV9oaXN0KClgLiBPdGhlciBjb21tb24gcGxvdHMgYXJlIGBnZW9tX2xpbmUoKWAsIGBnZW9tX2JhcigpYCwgYGdlb21fYm94cGxvdCgpYC4NCiAgICAqIGRlZmluZSBjb2xvciBzY2FsZXMsIHN1Y2ggYXMgYHNjYWxlX2NvbG9yX2JyZXdlcigpYCBvciBgc2NhbGVfY29sb3JfZGlzdGlsbGVyKClgLA0KICAgICogZmFjZXRpbmcgc3BlY2lmaWNhdGlvbnMgYGZhY2V0X3dyYXAoKWAgb3IgYGZhY2V0X2dyaWQoKWANCiAgICAqIGNvb3JkaW5hdGUgc3lzdGVtcywgc3VjaCBhcyBgY29vcmRfY2FydGVzaWFuKClgLCBgY29vcmRfZmxpcCgpYA0KDQpFdmVyeSBlbGVtZW50IGlzIHNlcGFyYXRlZCB3aXRoIGEgcGx1cyBzaWduICgqKisqKik6DQoNCmBgYHtyIGN1c3RvbSBnZ3Bsb3QsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTh9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSwgY29sb3VyID0gY2xhc3MpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAofm1hbnVmYWN0dXJlcikNCmBgYA0KDQpJdCdzIGltcG9ydGFudCB0byBtZW50aW9uIHRoYXQgdGhlIGFlc3RoZXRpY3MgY2FuIGJlIHBhc3NlZCBpbnNpZGUgdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24sIG9yIHdpdGhpbiBhIGdyYXBoaWMgcHJpbWl0aXZlLiBJbiB0aGUgZm9ybWVyLCB0aGUgYWVzdGhldGljcyBhcmUgdGhlIHNhbWUgZm9yIGFsbCB0aGUgbGF5ZXJzLCB3aGVyZWFzIGluIHRoZSBsYXR0ZXIsIHRoZSBhZXN0aGV0aWNzIHBhc3NlZCB0byBhIHNwZWNpZmljIGxheWVyIG9ubHkgYWZmZWN0IHRoYXQgbGF5ZXIuDQoNCmBgYHtyIGdncGxvdCBhZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aCgpDQoNCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5LGNvbG9yID0gY2xhc3MpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkNCmBgYA0KDQoNCldlIGNvdWxkIG5vdCBjb3ZlciBhbGwgdGhlIGRpZmZlcmVudCB2YXJpYW50cyB0aGF0IGNhbiBiZSBhY2hpZXZlZCB3aXRoIGBnZ3Bsb3QyYCBoZXJlLCBldmVuIGlmIHdlIHRyaWVkLg0KDQo8YnI+DQoNCjxmb250IHNpemU9IjUiPiBDb250aW51ZSB0byBbRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpc10oI3VuZGVyc3RhbmQpIDwvZm9udD4gDQoNCiMjIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIHsjRURBfQ0KDQpWaXN1YWxseSBpbnNwZWN0aW5nIHlvdXIgZGF0YSBjYW4gZ2l2ZSB5b3UgaW5zaWdodCB0byB0aGVpciBkeW5hbWljcywgcGF0dGVybnMsIGFuZCBoaXN0b3JpYyBiZWhhdmlvci4gSG93ZXZlciwgYmVmb3JlIGdvaW5nIGludG8gdGhlIG1vZGVsbGluZyBwaGFzZSBvZiB0aGUgYW5hbHlzaXMsIHdlIG11c3QgcGVyZm9ybSB0aGUgKipleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIChFREEpKiouW0BXaWNraGFtMjAxNl0NCg0KRURBIGludm9sdmVzIG1ha2luZyBoeXBvdGhlc2lzIHJlZ2FyZGluZyB5b3VyIGRhdGEsIHRyYW5zZm9ybSBhbmQgdmlzdWFsbHkgaW5zcGVjdCBzdGF0aXN0aWNhbCBwcm9wZXJ0aWVzIG9mIGl0Lg0KDQpTb21lIG9mIHRoZSBtb3N0IGltcG9ydGFudCB0aGluZ3MgdG8gbm90ZSBvbiB0aGUgRURBIHByb2Nlc3MgYXJlOg0KDQoqIFdoYXQgdHlwZSBvZiB2YXJpYXRpb24gZG8gZWFjaCB2YXJpYWJsZSBoYXZlPw0KDQoqIFdoYXQgaXMgdGhlIGNvdmFyaWF0aW9uIGJldHdlZW4gdmFyaWFibGVzPw0KDQoqIEFyZSB0aGVyZSBvdXRsaWVycyBwcmVzZW50IGluIHRoZSBkYXRhPw0KDQoqIFdoYXQgdHlwZSBvZiBkaXN0cmlidXRpb24gZG8gdGhlIHZhcmlhYmxlcyBmb2xsb3c/DQoNClNvbWUgZXhhbXBsZXMNCg0KRm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyAoZmFjdG9ycyksIHdlIGNhbiB1c2UgYSBiYXIgY2hhcnQ6DQpgYGB7ciBiYXJwbG90fQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKw0KICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjdXQpKSArDQogIGdndGl0bGUoIkNvdW50IG9mIERpYW1vbmRzIGJ5IGN1dCBxdWFsaXR5IikNCmBgYA0KDQpGb3IgY29udGludW91cyB2YXJpYWJsZXMsIGEgaGlzdG9ncmFtIGNhbiBiZSB1c2VkOg0KDQpgYGB7ciBoaXN0fQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKw0KICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSBjYXJhdCksIGJpbndpZHRoID0gMC41KSArDQogIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBjYXJhdHMiKQ0KYGBgDQoNCklmIHlvdSB3YW50IHRvIGFuYWx5emUgdGhlIGhpc3RvZ3JhbSBvZiBtdWx0aXBsZSB2YXJpYWJsZXMgaW4gb25lIHNpbmdsZSBwbG90LCB5b3UgY2FuIHVzZSBgZ2VvbV9mcmVxcG9seSgpYCBvciB1c2UgZmFjZXR0aW5nOg0KDQpgYGB7ciBmcmVxcG9seX0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMgJT4lIGZpbHRlcihjYXJhdCA8IDMpLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgY29sb3VyID0gY3V0KSkgKw0KICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4xKQ0KYGBgDQoNCmBgYHtyIGhpc3QgKyBmYWNldH0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMgJT4lIGZpbHRlcihjYXJhdCA8IDMpLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgZmlsbCA9IGN1dCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEpICsNCiAgZmFjZXRfd3JhcCh+IGN1dCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KQ2hvb3NpbmcgdGhlIGJpbndpZHRoIG9mIHRoZSBoaXN0b2dyYW0gY2FuIHRlbGwgZGlmZmVyZW50IHN0b3JpZXMgb3IgY2FuIHJldmVhbCBkaWZmZXJlbnQgcGF0dGVybnM6DQoNCmBgYHtyIGhpc3QgYmlud2lkdGgsIGZpZy53aWR0aD04LGZpZy5oZWlnaHQ9OH0NCmcgPC0gZ2dwbG90KGRhdGEgPSBkaWFtb25kcyAlPiUgZmlsdGVyKGNhcmF0IDwgMyksIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSkNCmcwIDwtIGcgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSkgKw0KICBnZ3RpdGxlKCJCaW53aWR0aCA9IDAuNSIpDQpnMSA8LSBnICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEpICsNCiAgZ2d0aXRsZSgiQmlud2lkdGggPSAwLjEiKQ0KZzIgPC0gZyArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSkgKw0KICBnZ3RpdGxlKCJCaW53aWR0aCA9IDAuMDEiKQ0KZzMgPC0gZzAvZzEvZzINCmczICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkhpc3RvZ3JhbXMgdmFyeWluZyB0aGUgYmlud2lkdGgiLA0KICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiRGlmZmVyZW50IHBhdHRlcm5zIGNhbiBhcmlzZSB3aGVuIHNlbGVjdGluZyBkaWZmZXJlbnQgYmlud2lkdGhzIikNCmBgYA0KDQpBbm90aGVyIG9wdGlvbiBpcyB0byBnbyB3aXRoIGJveHBsb3RzOg0KDQpgYGB7ciBib3hwbG90fQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsNCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwgeSA9IGh3eSkpKyBsYWJzKHggPSAiY2xhc3MiLCB5ID0gImh3eSBtcGciKQ0KYGBgDQoNCg0KQWdhaW4sIHRoZXNlIGFyZSBqdXN0IHNvbWUgZXhhbXBsZXMsIGJ1dCB0aGUgbGlzdCBvZiAgZ29lcyBvbiBhbmQgb24uDQoNCiFbXShpbWFnZXMvZmVhc3RzLlBORyl7d2lkdGg9MTUlfQ0KDQpSZWdhcmRpbmcgdGltZSBzZXJpZXMgYW5hbHlzaXMsIHRoZSBbYGZlYXN0c2BdKGh0dHBzOi8vZmVhc3RzLnRpZHl2ZXJ0cy5vcmcvKSBwYWNrYWdlICgqKkYqKmVhdHVyZSAqKkUqKnh0cmFjdGlvbiBhbmQgKipTKip0YXRpc3RpY3MgZm9yICoqVCoqaW1lICoqUyoqZXJpZXMpIGhhcyBtYW55IGZ1bmN0aW9ucyB0aGF0IG1ha2VzIGl0IGVhc3kgZm9yIHVzIHRvIGdldCBmdXJ0aGVyIGluc2lnaHQgaW50byBvdXIgdGltZSBzZXJpZXMsIFdlIGNhbiBnZXQgbWFueSBncmFwaHMgZnJvbSBpdCwgc3VjaCBhczoNCg0KYGBge3Igc2Vhc29uIHBsb3R9DQphdXNfcHJvZHVjdGlvbiAlPiUgZ2dfc2Vhc29uKEJlZXIpDQpgYGANCg0KYGBge3IgbGFnIHBsb3R9DQphdXNfcHJvZHVjdGlvbiAlPiUgZmlsdGVyKHllYXIoUXVhcnRlcikgPiAxOTkxKSAlPiUgZ2dfbGFnKEJlZXIpDQpgYGANCg0KYGBge3Igc3RsIGRlY29tcH0NCmF1c19wcm9kdWN0aW9uICU+JQ0KICBtb2RlbChTVEwoQmVlciB+IHNlYXNvbih3aW5kb3cgPSAicGVyaW9kaWMiKSkpICU+JSANCmNvbXBvbmVudHMoKSAlPiUgDQogIGF1dG9wbG90KCkNCmBgYA0KDQpXZSBjYW4gYWxzbyBnZXQgc3RhdGlzdGljcyBmcm9tIHRoaXMgcGFja2FnZToNCg0KYGBge3Igc3RsIGZlYXR1cmVzfQ0KYXVzX3JldGFpbCAlPiUNCiAgZmVhdHVyZXMoVHVybm92ZXIsIGZlYXRfc3RsKQ0KYGBgDQoNCg0KDQoNCjxicj4NCg0KPGZvbnQgc2l6ZT0iNSI+IEdvIHRvIFtNb2RlbF0oI3dvcmtmbG93KSA8L2ZvbnQ+IA0KDQojIyMgTW9kZWwNCg0KV2hlbmV2ZXIgd2UgdHJ5IHRvIG1vZGVsIGEgdmFyaWFibGUgb3IgcGhlbm9tZW5vbSwgaXQgaXMgc2FpZCB0aGF0IHdlIGFyZSB0cnlpbmcgdG8gZ2V0IGEgc2ltcGxpZmllZCB2ZXJzaW9uIG9mIHJlYWxpdHkuIEluIGZhY3QsIGl0J3Mgc2ltcGxlciB0aGFuIHRoYXQuIFdoYXQgd2UgYXJlIHJlYWxseSB0cnlpbmcgdG8gZG8gaXMgdW5kZXJzdGFuZCB0aGUgd2F5IGEgdmFyaWFibGUgb3Igc2V0IG9mIHZhcmlhYmxlcyBjaGFuZ2UsIHdoaWxlIGlnbm9yaW5nIG9yIGVsaW1pbmF0aW5nIGV4dGVybmFsICJub2lzZSIuDQoNCkl0IGlzIHdlbGwga25vd24gdG8gbW9zdCBkYXRhIHNjaWVudGlzdHMgdGhhdCAqKnlvdSBjYW5ub3QgdXNlIHRoZSBzYW1lIGRhdGEgZm9yIG1vZGVsbGluZyBhbmQgdGVzdGluZyAob3IgZm9yZWNhc3RpbmcpKiouIFRoYXQncyB3YXkgbWFueSBwZW9wbGUgc3BsaXQgdGhlaXIgZGF0YSBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0LiBIb3dldmVyLCBhIHRydWUgZGF0YSBzY2llbnRpc3QgaGFzIHRvIGJlIGV2ZW4gc3RyaWN0ZXIgb24gaXRzIHVzZSBvZiBkYXRhOg0KDQpJdCBpcyByZWNvbW1lbmRlZCB0aGF0IHlvdSBzcGxpdCB5b3VyIGRhdGEgaW50byB0aHJlZSBzZXRzOg0KDQo8c3R5bGU+DQpkaXYuYXF1YW1hcmluZSB7IGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmU7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDsgY29sb3I6IGJsYWNrfQ0KPC9zdHlsZT4NCjxkaXYgY2xhc3MgPSAiYXF1YW1hcmluZSI+DQoNCioqMS4qKiB+IDYwJSBvZiB5b3VyIGRhdGEgZ29lcyB0byB0aGUgKip0cmFpbmluZyoqIHNldC4gSGVyZSwgeW91IGNhbiB2aXN1YWxpemUgaXQsIHBlcmZvcm0gYWxsIHRoZSBtb2RlbCBmaXR0aW5nIGFuZCB0d2Vha2luZyB5b3Ugd2FudCwgb3ZlciBhbmQgb3ZlciBhZ2Fpbi4NCg0KKioyLioqIH4gMjAlIG9mIHlvdXIgZGF0YSBzaG91bGQgZ28gdG8gYSAqKnF1ZXJ5Kiogc2V0LiBXaXRoIHRoaXMgcXVlcnkgc2V0LCB5b3UgY2FuIGNvbXBhcmUgbW9kZWxzIGJ5IGhhbmQgYW5kIHZpc3VhbGl6ZSB0aGUgb3V0Y29tZXMuDQoNCioqMy4qKiBUaGUgfiAyMCUgIHJlbWFpbmluZyBkYXRhIHdvdWxkIGNvbmZvcm0gdGhlICoqdGVzdCoqIHNldC4gT25jZSB5b3UndmUgY29tcGFyZWQgYWxsIHlvdXIgbW9kZWxzIHdpdGggdGhlIHRyYWluaW5nIGFuZC9vciBxdWVyeSBzZXRzLCB5b3UgY2FuIHRlc3QgeW91ciBmaW5hbCBtb2RlbC4gVGhpcyB0ZXN0IGNhbiBvbmx5IGJlIHBlcmZvcm1lZCAqKk9OQ0UqKi4gVGhpcyBlbnN1cmVzIG5vIGJpYXMgaXMgaW50cm9kdWNlZCBpbiB0aGUgbW9kZWwgYW5kIGl0IHJlbWFpbnMgYSB0cnVlIGZvcmVjYXN0Lg0KDQo8L2Rpdj4NCg0KPGJyPg0KDQpXZSB3aWxsIGdvIHRocm91Z2ggbWFueSBkaWZmZXJlbnQgbW9kZWxzIGFpbWVkIHRvIHByb3ZpZGUgZm9yZWNhc3RzIGZvciBkaWZmZXJlbnQgc2l0dWF0aW9ucy4NCg0KDQoNCg0KVGhlIGZhbWlseSBvZiBtb2RlbHMgd2Ugd2lsbCBiZSBzdHVkeWluZyB0aHJvdWdob3V0IHRoZSBjb3Vyc2UgYXJlIHRoZSBmb2xsb3dpbmc6DQoNCjEuIFRpbWUgc2VyaWVzIGxpbmVhciBtb2RlbHMgKFRTTE0pLg0KMi4gRGVjb21wb3NpdGlvbiBtb2RlbHMuDQozLiBFeHBvbmVudGlhbCBzbW9vdGhpbmcgKEVUUykuDQo0LiBBUklNQS4NCjUuIER5bmFtaWMgcmVncmVzc2lvbiBtb2RlbHMuDQo2LiBJbnRyb2R1Y3Rpb24gdG8gYHRpZHltb2RlbHNgLg0KDQpUaGVyZSBhcmUgbWFueSB3YXlzIHRvIHRyYWluIG1vZGVscyBpbiAqKlIqKi4gQXMgd2Ugc2FpZCBiZWZvcmUsIHdlIHdpbGwgYmUgdXNpbmcgcHJpbWFyaWx5IHR3byBwYWNrYWdlcyBmb3IgdGhpczoNCg0KIVtdKGltYWdlcy9mYWJsZS5wbmcpe3dpZHRoPTE1JX0NCg0KKiBXaGl0aW4gdGhlIGB0aWR5dmVydHNgLCB3ZSB3aWxsIHVzZSB0aGUgW2BmYWJsZWBdKGh0dHBzOi8vZmFibGUudGlkeXZlcnRzLm9yZy8pIHBhY2thZ2UsIHdoaWNoIGhhcyBtYW55IHVuaXZhcmlhdGUgYW5kIG11bHRpdmFyaWF0ZSB0aW1lIHNlcmllcyBmb3JlY2FzdGluZyBtb2RlbHMuIFRoZSB3YXkgdG8gZml0IG9uZSBvciBtb3JlIG1vZGVscyBpbiB0aGUgYGZhYmxlYCBwYWNrYWdlIGlzIGJ5IHNwZWNpZnlpbmcgdGhlbSBpbiB0aGUgYG1vZGVsKClgIGZ1bmN0aW9uLltASHluZG1hbjIwMTldDQoNCmBgYHtyIG1vZGVsfQ0KZml0IDwtIGF1c19yZXRhaWwgJT4lDQogIGZpbHRlcigNCiAgICBTdGF0ZSAlaW4lIGMoIk5ldyBTb3V0aCBXYWxlcyIsICJWaWN0b3JpYSIpLA0KICAgIEluZHVzdHJ5ID09ICJEZXBhcnRtZW50IHN0b3JlcyINCiAgKSAlPiUgDQogIG1vZGVsKA0KICAgIGV0cyA9IEVUUyhib3hfY294KFR1cm5vdmVyLCAwLjMpKSwNCiAgICBhcmltYSA9IEFSSU1BKGxvZyhUdXJub3ZlcikpLA0KICAgIHNuYWl2ZSA9IFNOQUlWRShUdXJub3ZlcikNCiAgKQ0KZml0DQojIEEgbWFibGU6IDIgeCA1DQojIEtleTogICAgIFN0YXRlLCBJbmR1c3RyeSBbMl0NCiMgICBTdGF0ZSAgICAgICAgICAgSW5kdXN0cnkgICAgICAgICAgZXRzICAgICAgICAgIGFyaW1hICAgICAgICAgICAgICAgICAgIHNuYWl2ZSANCiMgICA8Y2hyPiAgICAgICAgICAgPGNocj4gICAgICAgICAgICAgPG1vZGVsPiAgICAgIDxtb2RlbD4gICAgICAgICAgICAgICAgIDxtb2RlbD4NCiMgMSBOZXcgU291dGggV2FsZXMgRGVwYXJ0bWVudCBzdG9yZXMgPEVUUyhBLEFkLEF+IDxBUklNQSgyLDEsMSkoMiwxLDEpWzF+IDxTTkFJVn4NCiMgMiBWaWN0b3JpYSAgICAgICAgRGVwYXJ0bWVudCBzdG9yZXMgPEVUUyhBLEEsQSk+IDxBUklNQSgyLDEsMSkoMSwxLDIpWzF+IDxTTkFJVn4NCmBgYA0KDQpUaGlzIHByb2R1Y2VzIGEgYG1hYmxlYCAoYSBtb2RlbCB0YWJsZSksIHdoZXJlIGV2ZXJ5IGNlbGwgaXMgYSBmaXR0ZWQgbW9kZWwuDQoNClVzaW5nIHRoZSBgcmVwb3J0KClgIGZ1bmN0aW9uLCB5b3UgY2FuIGdldCBhIGRldGFpbGVkIHZpZXcgb2YgYSBwYXJ0aWN1bGFyIG1vZGVsLg0KDQpgYGB7ciByZXBvcnR9DQpmaXQgJT4lIA0KICBmaWx0ZXIoU3RhdGUgPT0gIlZpY3RvcmlhIikgJT4lIA0KICBzZWxlY3QoU3RhdGUsSW5kdXN0cnksYXJpbWEpICU+JSANCiAgcmVwb3J0KCkNCmBgYA0KDQoNCllvdSBjb3VsZCB1c2UgdGhlIGB0aWR5KClgIGZ1bmN0aW9uIHRvIHJldHJpZXZlIGVhY2ggbW9kZWwncyB0ZXJtcywgZXN0aW1hdGVzIGFuZCBzdGF0aXN0aWNzIGluIGEgdGlkeSB3YXkuDQoNCmBgYHtyIHRpZHkgcmVwb3J0fQ0KdGlkeShmaXQpDQpgYGANCg0KDQpgYGB7ciByZXNpZHVhbHN9DQpmaXQgJT4lDQogIGZpbHRlcihTdGF0ZSA9PSAiVmljdG9yaWEiKSAlPiUgDQogIHNlbGVjdChTdGF0ZSxJbmR1c3RyeSxhcmltYSkgJT4lDQogIGdnX3RzcmVzaWR1YWxzKCkgKyBnZ3RpdGxlKCJSZXNpZHVhbCBEaWFnbm9zdGljcyBmb3IgdGhlIEFSSU1BIG1vZGVsIGZpdHRlZCB0byBWaWN0b3JpYSIpDQpgYGANCg0KV2l0aCB0aGUgYGdsYW5jZSgpYCBmdW5jdGlvbiB3ZSBjYW4gZ2V0IGZ1cnRoZXIgaW5mb3JtYXRpb24gZm9yIGV2ZXJ5IG1vZGVsIGZpdHRlZC4NCg0KYGBge3IgbW9kZWwgZml0fQ0KZ2xhbmNlKGZpdCkNCmBgYA0KDQoNCiAgDQo8YnI+DQoNCjxmb250IHNpemU9IjUiPiBHbyB0byBbRm9yZWNhc3RpbmddKCN3b3JrZmxvdykgPC9mb250PiANCg0KIyMgNC4gRm9yZWNhc3RpbmcgeyNmb2NzdH0NCg0KIVtdKGltYWdlcy9mYWJsZS5QTkcpe3dpZHRoPTE1JX0NCg0KT25jZSB3ZSBoYXZlIHRyYWluZWQgb25lIG9yIG1vcmUgbW9kZWxzIHRoYXQgaGF2ZSBhIHByb3BlciBmaXQgdG8gb3VyIHRyYWluaW5nIGRhdGEsIHdlIGNhbiBub3cgZ28gYWhlYWQgYW5kIHByb2R1Y2UgZm9yZWNhc3RzLiBVc2luZyBgZmFibGVgIGlzIGFzIHNpbXBsZSBhcyBydW5uaW5nIHRoZSBgZm9yZWNhc3QoKWAgZnVuY3Rpb24gdG8gb3VyIGBtYWJsZWAuIEl0J3MgYWxzbyBxdWl0ZSBlYXN5IHRvIGdldCBwbG90cyBmcm9tIGl0Og0KDQpgYGB7ciBmb3JlY2FzdCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OH0NCmZjc3QgPC0gZml0ICU+JSANCiAgZm9yZWNhc3QoaCA9ICIyIHllYXJzIikgDQpmY3N0DQpmY3N0ICU+JSANCiAgYXV0b3Bsb3QoZmlsdGVyKGF1c19yZXRhaWwsIHllYXIoTW9udGgpID4gMjAxMCksIGxldmVsID0gTlVMTCkgKyANCiAgZ2d0aXRsZSgiRm9yZWNhc3QgY29tcGFyaXNvbiBhY3Jvc3MgbW9kZWxzIGFuZCB0aW1lIHNlcmllcyIpDQpmY3N0ICU+JSANCiAgZmlsdGVyKC5tb2RlbCA9PSJldHMiKSAlPiUgDQogIGF1dG9wbG90KGZpbHRlcihhdXNfcmV0YWlsLCB5ZWFyKE1vbnRoKSA+IDIwMTApKSArIA0KICBnZ3RpdGxlKCJGb3JlY2FzdCAod2l0aCBwcmVkaWN0aW9uIGludGVydmFscykgZm9yIHRoZSBFVFMgbW9kZWwiKQ0KYGBgDQoNCg0KIyMgNS4gQ29tbXVuaWNhdGUNCg0KVGhlIGxhc3Qgc3RlcCBvZiB0aGUgd29ya2Zsb3cgaXMgdG8gY29tbXVuaWNhdGUgeW91ciBmaW5kaW5ncy4gVGhpcyBjYW4gYmUgZG9uZSBpbiBtYW55IGRpZmZlcmVudCB3YXlzLCBkZXBlbmRpbmcgb24geW91ciB0YXJnZXQgYXVkaWVuY2U6DQoNCiFbXShpbWFnZXMvc2hpbnkucG5nKXt3aWR0aD0xNSV9DQoNCiogSWYgeW91IHdhbnQgdG8gcHJlc2VudCByZXN1bHRzIHRvIHlvdXIgY29tcGFueSBhbmQgcHJvZHVjZSBhIHJlcHJvZHVjaWJsZSB0b29sIHRoYXQgY2FuIGJlIHVzZSBpbiBwcm9kdWN0aW9uLCB5b3UgY291bGQgbWFrZSBhIFtgU2hpbnlgXShodHRwczovL3NoaW55LnJzdHVkaW8uY29tLykgYXBwLg0KDQohW10oaW1hZ2VzL3JtYXJrZG93bi5wbmcpe3dpZHRoPTE1JX0NCg0KW2BSIE1hcmtkb3duYF0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBkb2N1bWVudHMgKGp1c3QgYXMgdGhlIG9uZSB5b3UncmUgcmVhZGluZyByaWdodCBub3cpIGFyZSBhIHdheSB0byBtYWtlIGRhdGEgc2NpZW5jZSByZXBvcnRzIHRoYXQgam9pbiBjb2RlLCBvdXRwdXRzIGFuZCBuYXJyYXRpdmUuIEdpdmVuIHRoYXQgaXQncyBiYXNlZCBvbiBtYXJrZG93biwgeW91IGNhbiB3cml0ZSAkXExhVGVYJCBlcXVhdGlvbnMsIGFuZCBzbyBvbi4NCg0KJCQNClxpbnRfezB9XntcaW5mdHl9IGVeey1zIFxjZG90IHR9IGYodCkgZCB0PVxsaW0gX3toIFxyaWdodGFycm93IFxpbmZ0eX0gXGludF97MH1ee2h9IGVeey1zLCB0fSBmKHQpIGQgdA0KJCQNCg0KKiBJZiB5b3Ugd2FudCB0byBwdWJsaXNoIHlvdXIgZmluZGluZ3MgZm9yIHRoZSBzY2llbnRpZmljIGNvbW11bml0eSwgeW91IGNvdWxkIHdyaXRlIGEgcGFwZXIuDQoNCiogWW91IGNvdWxkIGFsc28gcHVibGlzaCBpdCBvbmxpbmUgZm9yIGFueW9uZSB0byBhY2Nlc3MgaXQuDQoNCg0KIyMgUHJvZ3JhbW1pbmcNCg0KSW4gcHJhY3RpY2UsIHlvdSBjb3VsZCBhdXRvbWF0ZSB0aGUgZm9yZWNhc3Rpbmcgd29ya2Zsb3cgYnkgZGVmaW5pbmcgZnVuY3Rpb25zIGFuZCBpdGVyYXRpb25zLiBUaGUgY29tcGxldGUgY29kZSB0aGF0IGdlbmVyYXRlcyBvdXIgYW5hbHlzaXMsIGZyb20gaW1wb3J0aW5nIGRhdGEsIGFsbCB0aGUgd2F5IHRvIGNvbW11bmljYXRpbmcgeW91ciBmaW5kaW5ncyBpcyBhICoqcHJvZ3JhbSoqLg0KDQohW10oaW1hZ2VzL3B1cnJyLnBuZyl7d2lkdGg9MTUlfQ0KDQpUaGUgW2BwdXJycmBdKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgaXMgYW4gdXBncmFkZSB0byBSJ3MgZnVuY3Rpb25hbCBwcm9ncmFtbWluZyB0b29sa2l0Lg0KDQohW10oaW1hZ2VzL3BpcGUucG5nKXt3aWR0aD0xNSV9DQoNCklmIHlvdSdyZSBmYW1pbGlhciB3aXRoIGJhc2UgKipSKiogcHJvZ3JhbW1pbmcsIHlvdSBtaWdodCBoYXZlIHdvbmRlcmVkIGFib3V0IHRoZSBgICU+JSBgIG9wZXJhdG9yIHVzZWQgaW4gc29tZSBvZiB0aGUgZXhhbXBsZXMgYWJvdmUuIGAgJT4lIGAgaXMgY2FsbGVkIHRoZSAqKnBpcGUgb3BlcmF0b3IqKi4gSXQncyBwdXJwb3NlIGlzIGEgdG9vbCB0byBoZWxwIHlvdSBleHByZXNzIGEgc2VxdWVuY2Ugb2YgZnVuY3Rpb25zIG9yIG9wZXJhdGlvbnMgaW4gYSBtb3JlIGVsZWdhbnQgYW5kIHVuZGVyc3RhbmRhYmxlIHdheSwgYW5kIHdoZW4gcmVhZGluZyBjb2RlLCB5b3UgY291bGQgdHJhbnNsYXRlIGAgJT4lIGAgdG8gInRoZW4iLg0KDQpGb3IgZXhhbXBsZSwgaWYgeW91IGhhdmUgYW5kIEV4Y2VsIGJhY2tncm91bmQsIHlvdSBtaWdodCBoYXZlIGhhZCBjYXNlcyB3aGVyZSB5b3VyIG5lc3RlZCBmb3JtdWxhcyB3aGVyZSBxdWl0ZSBhIGNoYWxsZW5nZSB0byB1bmRlcnN0YW5kIGluIHRoZSBsb25nIHJ1biwgZm9yIGV4YW1wbGU6DQoNCmBgYA0KPVNJLkVSUk9SKElORElDRShDb3N0b3NbQ29zdG9dLENPSU5DSURJUigiU3VwcGxpZXMgLSBJbWFnaW5nICImW0BNb2RlbG9dLENvc3Rvc1tHcm91cF0mQ29zdG9zW01vZGVsXSwwKSkqW0BbVW5pZGFkZXMgZGUgaW1hZ2VuIHRvdGFsZXNdXSwiIikNCmBgYA0KDQpUaGUgc2FtZSB0aGluZyBjb3VsZCBoYXBwZW4gaW4gKipSKiouIEZvciBleGFtcGxlOg0KDQpgYGB7ciBtZXNzeSBjb2RlLCBldmFsPUZBTFNFfQ0KZml0IDwtIG1vZGVsKGZpbHRlcihhdXNfcmV0YWlsLFN0YXRlICVpbiUgYygiTmV3IFNvdXRoIFdhbGVzIiwgIlZpY3RvcmlhIiksIEluZHVzdHJ5ID09ICJEZXBhcnRtZW50IHN0b3JlcyIpLGV0cyA9IEVUUyhib3hfY294KFR1cm5vdmVyLCAwLjMpKSwgYXJpbWEgPSBBUklNQShsb2coVHVybm92ZXIpKSwgc25haXZlID0gU05BSVZFKFR1cm5vdmVyKSkNCmZpdA0KYGBgDQoNCkl0IGJlY29tZXMgdmVyeSBjb21wbGljYXRlZCB0byB1bmRlcnN0YW5kIHdoYXQgdGhlIGNvZGUgaXMgZG9pbmcgYW5kIGluIHdoaWNoIG9yZGVyLiBBcyBhIG1hdHRlciBvZiBmYWN0LCB0aGlzIGdpdmVzIGV4YWN0bHkgdGhlIHNhbWUgcmVzdWx0IGFzIHRoZSBjb2RlIHdlIHVzZWQgaW4gdGhlICoqTW9kZWwqKiBzZWN0aW9uLg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmZpdCA8LSBhdXNfcmV0YWlsICU+JQ0KICBmaWx0ZXIoDQogICAgU3RhdGUgJWluJSBjKCJOZXcgU291dGggV2FsZXMiLCAiVmljdG9yaWEiKSwNCiAgICBJbmR1c3RyeSA9PSAiRGVwYXJ0bWVudCBzdG9yZXMiDQogICkgJT4lIA0KICBtb2RlbCgNCiAgICBldHMgPSBFVFMoYm94X2NveChUdXJub3ZlciwgMC4zKSksDQogICAgYXJpbWEgPSBBUklNQShsb2coVHVybm92ZXIpKSwNCiAgICBzbmFpdmUgPSBTTkFJVkUoVHVybm92ZXIpDQogICkNCmBgYA0KDQpSZWFkaW5nIGl0IHdpdGggdGhlIHBpcGUgb3BlcmF0b3IsIHdvdWxkIGJlIGxpa2U6DQoNCjEuIERlZmluZSBhIHZhcmlhYmxlIGNhbGxlZCAiZml0Ii4NCg0KMi4gVGFrZSB0aGUgImF1c19yZXRhaWwiIGRhdGE7IGB0aGVuYA0KDQozLiBmaWx0ZXIgaXQsIHNvIHRoYXQgd2Uga2VlcCBvbmx5IHRoZSBzdGF0ZXMgIk5ldyBTb3V0aCBXYWxlcyIgYW5kICJWaWN0b3JpYSIgYW5kIHRoZSBpbmR1c3RyeSBpcyAiRGVwYXJ0bWVudCBzdG9yZXMiOyBgdGhlbmANCg0KNC4gRml0IHRoZSB0aHJlZSBtb2RlbHM6IEVUUywgQVJJTUEgYW5kIFNlYXNvbmFsIE5hw692ZS4NCg0KSXQgaXMgbm90IHRoZSBwdXJwb3NlIG9mIHRoaXMgY291cnNlIHRvIGdvIGluLWRlcHRoIGluIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcgb3IgbGVhcm5pbmcgdG8gZG8gdGlkeSBpdGVyYXRpb25zLCBidXQgd2UgbWF5IGRvIHNvbWUgZXhhbXBsZXMgYWxvbmcgdGhlIHdheS4NCg0KIyB7LX0NCg0KDQo8YnI+PGJyPjxicj48YnI+DQoNCioqKg0KDQojIEV4YW1wbGVzIA0KDQpMZXQncyBwdXQgaW4gcHJhY3RpY2Ugd2hhdCB3ZSd2ZSBzZWVuIHNvIGZhciB3aXRoIHNvbWUgZXhhbXBsZXMuDQoNCiMjIEEpIEZvcmVjYXN0aW5nIFBvcHVsYXRpb24NCg0KV2UgYXJlIHRhc2tlZCB3aXRoIHByb2R1Y2luZyBmb3JlY2FzdHMgb2YgdGhlIHJ1cmFsIHBvcHVsYXRpb24gYXMgYSBwZXJjZW50YWdlIGluIE1leGljbywgQnJhemlsIGFuZCBBcmdlbnRpbmEuIFdlIHdlcmUgcHJvdmlkZWQgd2l0aCBhIGRhdGFzZXQgZG93bmxvYWRlZCBmcm9tIHRoZSBXb3JsZCBCYW5rLiBMZXQncyBpbXBvcnQgdGhlIHdvcmxkX2JhbmtfcG9wLmNzdiBmaWxlIHVzaW5nIGByZWFkcmA6DQoNCiMjIyAxLiBJbXBvcnQgZGF0YSANCg0KYGBge3IgcG9wIGRhdGF9DQpwb3AgPC0gcmVhZF9jc3YoIi4vZGF0YS93b3JsZF9iYW5rX3BvcC5jc3YiKQ0KIyBJZiB5b3UgZG9uJ3QgaGF2ZSB0aGUgY3N2IGZpbGUsIHlvdSBjYW4gcnVuOg0KIyBkYXRhKHdvcmxkX2JhbmtfcG9wLCBwYWNrYWdlID0gInRpZHlyIikNCiMgcG9wIDwtIHdvcmxkX2JhbmtfcG9wDQpwb3ANCiMgQSB0aWJibGU6IDEsMDU2IHggMjANCiMgICAgY291bnRyeSBpbmRpY2F0b3IgYDIwMDBgIGAyMDAxYCBgMjAwMmAgYDIwMDNgICBgMjAwNGAgIGAyMDA1YCAgIGAyMDA2YCAgIGAyMDA3YA0KIyAgICA8Y2hyPiAgIDxjaHI+ICAgICAgPGRibD4gIDxkYmw+ICA8ZGJsPiAgPGRibD4gICA8ZGJsPiAgIDxkYmw+ICAgIDxkYmw+ICAgIDxkYmw+DQojICAxIEFCVyAgICAgU1AuVVJCLlR+IDQuMjRlNCA0LjMwZTQgNC4zN2U0IDQuNDJlNCA0LjQ3ZSs0IDQuNDllKzQgIDQuNDllKzQgIDQuNDdlKzQNCiMgIDIgQUJXICAgICBTUC5VUkIuR34gMS4xOGUwIDEuNDFlMCAxLjQzZTAgMS4zMWUwIDkuNTFlLTEgNC45MWUtMSAtMS43OGUtMiAtNC4zNWUtMQ0KIyAgMyBBQlcgICAgIFNQLlBPUC5UfiA5LjA5ZTQgOS4yOWU0IDkuNTBlNCA5LjcwZTQgOS44N2UrNCAxLjAwZSs1ICAxLjAxZSs1ICAxLjAxZSs1DQojICA0IEFCVyAgICAgU1AuUE9QLkd+IDIuMDZlMCAyLjIzZTAgMi4yM2UwIDIuMTFlMCAxLjc2ZSswIDEuMzBlKzAgIDcuOThlLTEgIDMuODRlLTENCiMgIDUgQUZHICAgICBTUC5VUkIuVH4gNC40NGU2IDQuNjVlNiA0Ljg5ZTYgNS4xNmU2IDUuNDNlKzYgNS42OWUrNiAgNS45M2UrNiAgNi4xNWUrNg0KIyAgNiBBRkcgICAgIFNQLlVSQi5HfiAzLjkxZTAgNC42NmUwIDUuMTNlMCA1LjIzZTAgNS4xMmUrMCA0Ljc3ZSswICA0LjEyZSswICAzLjY1ZSswDQojICA3IEFGRyAgICAgU1AuUE9QLlR+IDIuMDFlNyAyLjEwZTcgMi4yMGU3IDIuMzFlNyAyLjQxZSs3IDIuNTFlKzcgIDIuNTllKzcgIDIuNjZlKzcNCiMgIDggQUZHICAgICBTUC5QT1AuR34gMy40OWUwIDQuMjVlMCA0LjcyZTAgNC44MmUwIDQuNDdlKzAgMy44N2UrMCAgMy4yM2UrMCAgMi43NmUrMA0KIyAgOSBBR08gICAgIFNQLlVSQi5UfiA4LjIzZTYgOC43MWU2IDkuMjJlNiA5Ljc3ZTYgMS4wM2UrNyAxLjA5ZSs3ICAxLjE1ZSs3ICAxLjIxZSs3DQojIDEwIEFHTyAgICAgU1AuVVJCLkd+IDUuNDRlMCA1LjU5ZTAgNS43MGUwIDUuNzZlMCA1Ljc1ZSswIDUuNjllKzAgIDQuOTJlKzAgIDQuODllKzANCiMgLi4uIHdpdGggMSwwNDYgbW9yZSByb3dzLCBhbmQgMTAgbW9yZSB2YXJpYWJsZXM6IGAyMDA4YCA8ZGJsPiwgYDIwMDlgIDxkYmw+LA0KIyAgIGAyMDEwYCA8ZGJsPiwgYDIwMTFgIDxkYmw+LCBgMjAxMmAgPGRibD4sIGAyMDEzYCA8ZGJsPiwgYDIwMTRgIDxkYmw+LA0KIyAgIGAyMDE1YCA8ZGJsPiwgYDIwMTZgIDxkYmw+LCBgMjAxN2AgPGRibD4NCmBgYA0KDQojIyMgMi4gVGlkeSBkYXRhDQoNCldlIGNhbiBpbW1lZGlhdGVseSBzZWUgdGhhdCBvdXIgZGF0YSBpc24ndCB0aWR5Og0KDQppKSBDb2x1bW5zIGRvIG5vdCByZXByZXNlbnQgdmFyaWFibGVzICh3ZSBuZWVkIHRvIHB1dCB0aGUgeWVhcnMgaW4gcm93cywgbm90IGNvbHVtbnMgd2l0aCBgcGl2b3RfbG9uZ2VyYCkuDQoNCmlpKSBXZSBtdXN0IHB1dCB2YXJpYWJsZXMgaW4gY29sdW1ucyAoY29sdW1uIGluZGljYXRvcikgd2l0aCBgcGl2b3Rfd2lkZXIoKWAuDQoNCmlpaSkgV2UgY2FuIGRpc2NhcmQgdGhlIHZhcmlhYmxlcyB0aGF0IHdvbid0IGJlIG5lZWRlZCB3aXRoIGBzZWxlY3QoKWAgaW4gY29uanVuY3Rpb24gd2l0aCBgY29udGFpbnMoKWAuDQoNCml2KSBXZSB3aWxsIHJlbmFtZSB0aGUgdmFyaWFibGVzIHdpdGggYHJlbmFtZSgpYC4NCg0KdikgV2Ugb25seSBoYXZlIHRvdGFsIHBvcHVsYXRpb24gYW5kIHVyYmFuIHBvcHVsYXRpb24gYW5kIHRoZSB2YXJpYWJsZXMgImNvdW50cnkiIGFuZCAieWVhciIgYXJlIGNoYXJhY3Rlci4gV2UgbXVzdCBjYWxjdWxhdGUgdGhlIHJ1cmFsIHBvcHVsYXRpb24gYXMgYSBwZXJjZW50YWdlIG9mIHRoZSB0b3RhbCwgd2l0aCBgbXV0YXRlKClgLiBIZXJlLCB3ZSB3aWxsIGFsc28gY29udmVydCB0aGUgImNvdW50cnkiIHZhcmlhYmxlIHRvIGZhY3RvciB1c2luZyBgYXNfZmFjdG9yKClgIGFuZCB0aGUgInllYXIiIHZhcmlhYmxlIHRvIGRvdWJsZSB1c2luZyBgYXMuaW50ZWdlcigpYC4NCg0KdmkpIFRoZXJlIGFyZSBtYW55IGNvdW50cmllcyB0aGF0IGFyZW4ndCBuZWVkZWQsIHNvIHdlIG5lZWQgdG8ga2VlcCBvbmx5IHRoZSBvbmVzIG9mIGludGVyZXN0IHdpdGggYGZpbHRlcigpYC4NCg0KdmlpKSBUaGUgZGF0YSBpcyBhIGB0aWJibGVgIG9iamVjdCwgd2UgbmVlZCB0byBjb252ZXJ0IGl0IHRvIGEgYHRzaWJibGVgIHVzaW5nIGBhc190c2liYmxlKClgLg0KDQpXZSBjYW4gZG8gdGhpcyBhbGwgaW4gb25lIHN0ZXAgdXNpbmcgdGhlIGAgJT4lIGAgKHBpcGUpIG9wZXJhdG9yLg0KDQpgYGB7ciBwb3AgdGlkeX0NCnBvcF90aWR5IDwtIHBvcCAlPiUgDQogICMgaSkNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhjb3VudHJ5LCBpbmRpY2F0b3IpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQ0KICAjIGlpKQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaW5kaWNhdG9yLA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHZhbHVlKSAlPiUgDQogICMgaWlpKQ0KICBzZWxlY3QoY291bnRyeSx5ZWFyLGNvbnRhaW5zKCJUT1RMIikpICU+JSANCiAgIyBpdikNCiAgcmVuYW1lKHVyYmFuX3BvcCA9IFNQLlVSQi5UT1RMLHRvdGFsX3BvcCA9IFNQLlBPUC5UT1RMKSAlPiUgDQogICN2KQ0KICBtdXRhdGUocnVyYWxfcG9wX3BjdCA9ICgxIC0gdXJiYW5fcG9wIC8gdG90YWxfcG9wKSoxMDAsDQogICAgICAgICBjb3VudHJ5ID0gYXNfZmFjdG9yKGNvdW50cnkpLA0KICAgICAgICAgeWVhciA9IGFzLmludGVnZXIoeWVhcikNCiAgICAgICAgICkgJT4lIA0KICAjIHZpKQ0KICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIk1FWCIsIkJSQSIsIkFSRyIpKSAlPiUgDQogICN2aWkpDQogIGFzX3RzaWJibGUoa2V5ID0gY291bnRyeSwgaW5kZXggPSB5ZWFyKQ0KDQpwb3BfdGlkeQ0KIyBBIHRzaWJibGU6IDU0IHggNSBbMVldDQojIEtleTogICAgICAgY291bnRyeSBbM10NCiMgICAgY291bnRyeSAgeWVhciB1cmJhbl9wb3AgdG90YWxfcG9wIHJ1cmFsX3BvcF9wY3QNCiMgICAgPGZjdD4gICA8aW50PiAgICAgPGRibD4gICAgIDxkYmw+ICAgICAgICAgPGRibD4NCiMgIDEgQlJBICAgICAgMjAwMCAxNDIzMTk0OTggMTc1Mjg3NTg3ICAgICAgICAgIDE4LjgNCiMgIDIgQlJBICAgICAgMjAwMSAxNDQ5NjEwMDQgMTc3NzUwNjcwICAgICAgICAgIDE4LjQNCiMgIDMgQlJBICAgICAgMjAwMiAxNDc1MDc2NTYgMTgwMTUxMDIxICAgICAgICAgIDE4LjENCiMgIDQgQlJBICAgICAgMjAwMyAxNTAwMDU4MDEgMTgyNDgyMTQ5ICAgICAgICAgIDE3LjgNCiMgIDUgQlJBICAgICAgMjAwNCAxNTI0NDgwMjMgMTg0NzM4NDU4ICAgICAgICAgIDE3LjUNCiMgIDYgQlJBICAgICAgMjAwNSAxNTQ4MzExMjcgMTg2OTE3MzYxICAgICAgICAgIDE3LjINCiMgIDcgQlJBICAgICAgMjAwNiAxNTcxNTA1OTAgMTg5MDEyNDEyICAgICAgICAgIDE2LjkNCiMgIDggQlJBICAgICAgMjAwNyAxNTk0MDc5MDggMTkxMDI2NjM3ICAgICAgICAgIDE2LjYNCiMgIDkgQlJBICAgICAgMjAwOCAxNjE2MTgwMDcgMTkyOTc5MDI5ICAgICAgICAgIDE2LjMNCiMgMTAgQlJBICAgICAgMjAwOSAxNjM3OTgzOTEgMTk0ODk1OTk2ICAgICAgICAgIDE2LjANCiMgLi4uIHdpdGggNDQgbW9yZSByb3dzDQpgYGANCg0KIyMjIDMuIFVuZGVyc3RhbmRpbmcgdGhlIGRhdGENCg0KV2l0aCBvdXIgdGlkeSBkYXRhLCB3ZSBjYW4gcHJvY2VlZCB3aXRoIHRoZSB2aXN1YWxpemF0aW9uIGFuZCBFREEuIFRoZSBlbmQgcmVzdWx0IHdpbGwgYmUgdGhlIHNlbGVjdGlvbiBvZiBvbmUgb3IgbW9yZSBmb3JlY2FzdGluZyBtb2RlbHMuDQoNClRoZSBgZmVhc3RzYCBwYWNrYWdlIHByb3ZpZGVzIHVzIHdpdGggYSBjb252ZW5pZW50IGZ1bmN0aW9uIGBhdXRvcGxvdCgpYCB0aGF0IGRldGVjdHMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgYHRzaWJibGVgIGFuZCBhcHBsaWVzIGEgYGdncGxvdGAgb2JqZWN0IHRvIHRoZSBkYXRhLg0KDQpXZSBzcGxpdCBvdXIgZGF0YSANCmBgYHtyIHBvcCBwbG90fQ0KcG9wX3RyYWluIDwtIHBvcF90aWR5ICU+JSANCiAgZmlsdGVyKHllYXIgPD0gMjAwOSkNCnBvcF9xdWVyeSA8LSBwb3BfdGlkeSAlPiUgDQogIGZpbHRlcih5ZWFyID4gMjAwOSAmIHllYXIgPD0gMjAxMykNCnBvcF90cmFpbl9xdWVyeSA8LSBwb3BfdGlkeSAlPiUgDQogIGZpbHRlcih5ZWFyIDw9IDIwMTMpDQojIFRvdGFsIHBvcHVsYXRpb24gcGxvdA0KcG9wX3RyYWluICU+JSANCiAgYXV0b3Bsb3QodG90YWxfcG9wKSArIGdndGl0bGUoIlRvdGFsIHBvcHVsYXRpb24iKSArIA0KICB5bGFiKCIiKQ0KIyBSdXJhbCBwb3B1bGF0aW9uDQpwb3BfdHJhaW4gJT4lDQogIGF1dG9wbG90KHJ1cmFsX3BvcF9wY3QpICsgZ2d0aXRsZSgiUnVyYWwgcG9wdWxhdGlvbiAoJSkiKSArIA0KICB5bGFiKCIiKQ0KYGBgDQoNClRoZSB0b3RhbCBwb3B1bGF0aW9uIGhhcyBhbiB1cHdhcmQgdHJlbmQgZm9yIE1leGljbyBhbmQgQnJhemlsLCBhbmQgaXQgc2VlbXMgZmFpcmx5IGNvbnN0YW50IGZvciBSdXNzaWEuDQoNClRoZSBydXJhbCBwb3B1bGF0aW9uIHNlZW1zIHRvIGJlIGRlY3JlYXNpbmcgaW4gYWxsIGNhc2VzLCB3aXRoIGEgc3RlZXBlciBzbG9wZSBpbiBib3RoIGFtZXJpY2FuIGNvdW50cmllcy4NCg0KV2Ugd2lsbCBmaXQgdGhyZWUgbW9kZWxzIHRvIGVhY2ggdGltZSBzZXJpZXM6DQoNCiogQSByYW5kb20gd2FsayBtb2RlbCB3aXRoIGRyaWZ0Lg0KKiBBIHNpbXBsZSBUU0xNLg0KKiBBbiBFVFMuDQoNCmBgYHtyIHBvcCBtb2RlbH0NCnBvcF9maXQgPC0gcG9wX3RyYWluICU+JSANCiAgbW9kZWwoYFJXIHcvIGRyaWZ0YCA9IFJXKHJ1cmFsX3BvcF9wY3QgfiBkcmlmdCgpKSwNCiAgICAgICAgYFRTTE0gdy8gdHJlbmRgID0gVFNMTShydXJhbF9wb3BfcGN0IH4gdHJlbmQoKSksDQogICAgICAgIEVUUyA9IEVUUyhydXJhbF9wb3BfcGN0IH4gZXJyb3IoIkEiKSArIHRyZW5kKCJBIikgKyBzZWFzb24oIk4iKSApDQogICAgICAgICkNCnRpZHkocG9wX2ZpdCkNCmBgYA0KDQojIyMgNC4gRm9yZWNhc3RpbmcNCg0KYGBge3IgcG9wIGZjc3QsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQ0KcG9wX2Zjc3QgPC0gcG9wX2ZpdCAlPiUgDQogIGZvcmVjYXN0KGggPSAiNCB5ZWFycyIpIA0KcG9wX2Zjc3QgJT4lIA0KICBhdXRvcGxvdChwb3BfdHJhaW5fcXVlcnkpICsNCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycygubW9kZWwpLCByb3dzID0gdmFycyhjb3VudHJ5KSwgc2NhbGVzID0gImZyZWVfeSIpICsgDQogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArDQogIHlsYWIoIlJ1cmFsIHBvcHVsYXRpb24gKCUpIikNCmBgYA0KDQpXZSBzZWUgdGhhdCB0aGUgbW9kZWxzIG92ZXIgZXN0aW1hdGUgdGhlIGRlY3JlYXNlIGluIHJ1cmFsIHBvcHVsYXRpb24uIFdlIGNoYW5nZSB0aGUgRVRTIG1vZGVsIHNsaWdodGx5IGFuZCBjaGVjayBpZiB3ZSBnZXQgYmV0dGVyIHJlc3VsdHMuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0NCnBvcF9maXQyIDwtIHBvcF90cmFpbiAlPiUgDQogIG1vZGVsKGBSVyB3LyBkcmlmdGAgPSBSVyhydXJhbF9wb3BfcGN0IH4gZHJpZnQoKSksDQogICAgICAgIGBUU0xNIHcvIHRyZW5kYCA9IFRTTE0ocnVyYWxfcG9wX3BjdCB+IHRyZW5kKCkpLA0KICAgICAgICBFVFMgPSBFVFMocnVyYWxfcG9wX3BjdCB+IGVycm9yKCJBIikgKyB0cmVuZCgiQWQiKSArIHNlYXNvbigiTiIpICkNCiAgICAgICAgKQ0KcG9wX2Zjc3QyIDwtIHBvcF9maXQyICU+JSANCiAgZm9yZWNhc3QoaCA9ICI0IHllYXJzIikgDQoNCnBvcF9mY3N0MiAlPiUgDQogIGF1dG9wbG90KHBvcF90cmFpbl9xdWVyeSkgKw0KICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKC5tb2RlbCksIHJvd3MgPSB2YXJzKGNvdW50cnkpLCBzY2FsZXMgPSAiZnJlZV95IikgKyANCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsNCiAgeWxhYigiUnVyYWwgcG9wdWxhdGlvbiAoJSkiKQ0KYGBgDQoNCmBgYHtyfQ0KYWNjdXJhY3kocG9wX2Zjc3QyLHBvcF90cmFpbl9xdWVyeSkgJT4lIA0KICBhcnJhbmdlKGNvdW50cnksIE1BUEUpDQpgYGANCg0KSXQgc2VlbXMgbm93IHRoYXQgd2UgaGF2ZSBhIGJldHRlciBhY2N1cmFjeSBvbiBvdXIgZm9yZWNhc3QsIGVzcGVjaWFsbHkgd2l0aCB0aGUgRVRTIG1vZGVsLiBXZSB3aWxsIHByb2NlZWQgdGhlbiBhbmQgbWFrZSBvdXIgY29tcGxldGUgZm9yZWNhc3Qgd2l0aCB0aGlzIEVUUyBtb2RlbC4NCg0KYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTh9DQpwb3BfdHJhaW4gJT4lIA0KICBtb2RlbChFVFMgPSBFVFMocnVyYWxfcG9wX3BjdCB+IGVycm9yKCJBIikgKyB0cmVuZCgiQWQiKSArIHNlYXNvbigiTiIpICkNCiAgICAgICAgKSAlPiUgDQogIGZvcmVjYXN0KGggPSAiMTIgeWVhcnMiKSAlPiUgDQogIGF1dG9wbG90KHBvcF90aWR5KSArIA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAyMDE0LCBsaW5ldHlwZSA9ImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsNCiAgeWxhYigiUnVyYWwgcG9wdWxhdGlvbiAoJSkiKSANCmBgYA0KDQojIyBCKSAyMDEzIE5ZQyBGbGlnaHRzDQoNClRoZSBgbnljZmxpZ2h0czEzYCBwYWNrYWdlIGNvbnRhaW5zIGFsbCB0aGUgZmxpZ2h0cyB0aGF0IGRlcGFydGVkIGZyb20gb25lIG9mIE5ZQydzIGFpcnBvcnRzIG9uIDIwMTMuDQoNCkl0IGNvbnRhaW5zIHNldmVyYWwgYHRpYmJsZXNgOg0KDQoqIGFpcmxpbmVzDQoNCiogYWlycG9ydHMNCg0KKiBmbGlnaHRzDQoNCiogcGxhbmVzDQoNCiogd2VhdGhlcg0KDQpXZSBjYW4gZ2V0IGEgc25lYWsgcGVhayBhdCB0aGVtIGJ5IGNhbGxpbmcgdGhlbS4NCg0KYGBge3J9DQphaXJsaW5lcw0KYWlycG9ydHMNCmZsaWdodHMNCnBsYW5lcw0Kd2VhdGhlcg0KYGBgDQoNCg0KKioqDQojIFJlZmVyZW5jZXMNCg0K