Load libraries

library(fpp3)
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────────────────────── fpp3 0.5 ──
✔ tibble      3.2.1     ✔ tsibble     1.1.3
✔ dplyr       1.1.3     ✔ tsibbledata 0.4.1
✔ tidyr       1.3.0     ✔ feasts      0.3.1
✔ lubridate   1.9.2     ✔ fable       0.3.3
✔ ggplot2     3.4.3     ✔ fabletools  0.3.3
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────── fpp3_conflicts ──
✖ lubridate::date()    masks base::date()
✖ dplyr::filter()      masks stats::filter()
✖ tsibble::intersect() masks base::intersect()
✖ tsibble::interval()  masks lubridate::interval()
✖ dplyr::lag()         masks stats::lag()
✖ tsibble::setdiff()   masks base::setdiff()
✖ tsibble::union()     masks base::union()
library(GGally)
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2
library(seasonal)

Attaching package: ‘seasonal’

The following object is masked from ‘package:tibble’:

    view

Transformations and adjustments

  • Per capita adjustments: Dividing data by population
global_economy |>
  filter(Country == "Australia") |>
  autoplot(GDP / Population)

Inflation adjustments

Australian newspapers adjusted for inlfation

## Retail CPI
aus_dataset <- aus_retail |>
  filter(Industry == "Newspaper and book retailing") |>
  group_by(Industry) |>
  index_by(Year = year(Month)) |>
  summarise(Turnover = sum(Turnover))

aus_dataset |> autoplot()
Plot variable not specified, automatically selected `.vars = Turnover`

  • We see the data going up untill around 2010 and then start dipping down.
  • Is this because a change in industry or just a change in the CPI?
  • Or is it because the CPI?
## CPI
aus_economy <- global_economy |>
  filter(Code == "AUS")

aus_economy

Joining the datasets

aus_dataset |>
  left_join(aus_economy, by="Year") |>
  mutate(Adjusted_Turnover = Turnover / CPI *100) |>
  pivot_longer(c(Turnover, Adjusted_Turnover), values_to = "Turnover") |>
  mutate(name=factor(name, levels = c("Turnover","Adjusted_turnover"))) |>
  ggplot(aes(x=Year,y=Turnover)) + geom_line() +
  facet_grid(name ~ ., scales = "free_y") +
  labs(title="Turnover: Australian print media industry", y="$AU")

  • We can see that adjusted for CPI the Turnover has been declining since 1980’s
  • Decline since 2010 is been faster, but has been going on since 1980

When forecasting you want to forecast taking in to account CPI

Mathematical Transformation

  • Take data and transform by applying a mathematical transformation
  • We try to make the variations in the series more HOMOGENEOUS
food <- aus_retail |>
  filter(Industry == "Food retailing") |>
  summarise(Turnover = sum(Turnover))
food

The original plot:

food |> autoplot(Turnover)

Square root:

Works, but variations at the start of series are still bigger than variations at the end

food |> autoplot(sqrt(Turnover)) + labs(title = "Square root Turnover")

Cube root

Works, but variations at the start of series are still bigger than variations at the end

food |> autoplot(Turnover^(1/3)) + labs(title = "Cube root Turnover")

Log

food |> autoplot(log(Turnover)) + labs(title = "Log Turnover")

  • Much better, variations are more HOMOGENEOUS
  • Size of fluctuations at the start are about same size at end

This is going to make modeling much easier Makes timeseries decompositions easier Good first step to remove that extra heterogeneity Allows us to fit a much simpler model

Inverse transformation

food |> autoplot(-1/Turnover) + labs(title = "-1/Turnover")

  • Gone too far, Log was about right

BOX-COX Transformations

Bickel-Doksum transformations

Function to compute the optimal lambda

food |> features(Turnover, features = guerrero)

This attempts to balance the seasonal fluctuations and random variation across the series Always check the results A low value of lambda can give extremely large prediction intervals

Using the guerrero transformation

food |> autoplot(box_cox(Turnover, 0.08952696)) +
  labs(title = "Box-Cox Transformed turnover")

  • Often no transformations needed
  • Simple transformations are easier to explain and work well enough
  • Can have a VERY LARGE effect on Prediction Interval
  • If some data are 0 or negative, then use lambda > 0
  • log1p() can also be useful for data with 0s
  • Choosing logs is a simple way to force forecasts to be positive
  • Transformations must be reversed to obtain forecasts on the original scale. (Handled automatically by fable)

Time series components

A time series decomposition involves estimating timeseries components

Decomposition: yt = f(St, Tt, Rt) where: * yt = data at period t * St = seasonal component at period t * Tt = trend at period t * Rt = remainder component at period t

Additive decomposition: yt = St + Tt + Rt Multiplicative decomposition: yt = St * Tt * Rt

Additive model

  • Appropiate if magnitude of seasonal fluctuations does not vary with level

  • If seasonal patterns are proportional to the level of the series -> MULTIPLICATIVE MODEL

  • Multiplicative decomposition more prevalent with economic series

  • ALTERNATIVE: Use a Box-Cox transformation, and then use additive decomposition

  • Logs turn multiplicative relationships in to additive relationships

yt = St * Tt * Rt --> ln(yt) = ln(St) + ln(Tt) + ln(Rt )

Example

us_employment |> 
  filter(Title == "Retail Trade", year(Month) >= 1990) |>
  select(-Series_ID) -> us_retail_employment_1990

us_retail_employment_1990 |> autoplot(Employed) + geom_point() +
  labs(y="Persons (thousands)", title = "Total employment in US retail")

  • We have Trend + Cycle + Seasonality, lets fit a model
us_retail_employment_1990 |>
  model(stl = STL(Employed))

Looking at the model components

dcmp <- us_retail_employment_1990 |>
  model(stl = STL(Employed))
components(dcmp)

Autoplot the components

components(dcmp) |> autoplot()

Other things we can do with the components

us_retail_employment_1990 |>
  autoplot(Employed, color="black") +
  autolayer(components(dcmp), trend, color="#D55E00") +
  labs(y= "Persons (thousands)", title = "Total employment in US retail")

Subseries plot Useful if interested in SHAPE of seasonality

components(dcmp) |> gg_subseries(season_year)

Sesonal Adjustment

  • Useful by-product of decomposition: an easy way to calculate seasonally adjusted data
  • Additive decomposition: seasonally adjusted data given by yt - St = Tt + Rt
  • Multiplicative: yt/St = Tt * Rt

Plotting the seasonally adjusted data: yt - St = Tt + Rt

us_retail_employment_1990 |>
  autoplot(Employed, color="black") +
  autolayer(components(dcmp), season_adjust, color="blue") +
  labs(y="Persons (thousands)", title = "Total employment in US retail")

Observations:

  • We can see the season_adjust (blue) line is more wobbly than the trend line in the above chart
  • This is due to the effect of Rt the remainder, which is almost always noisy in nature

How to seasonal adjust:

  • We use estimates of S based on past values to seasonally adjust a current value
  • Seasonally adjusted series reflect remainders as well as trend
  • Therefore they are not smooth, and downturns or upturns can be misleading
  • It is better to use the trend-cycle component to look for turning points

Moving Averages (Classical Decomposition)

A moving average is an average of nearby points

First step: Get and estimate of the trend component

  • We use Moving Averages for this:
global_economy |>
  filter(Country == "Australia") |>
  autoplot(Exports) +
  labs(y="% of GDP", title = "Total Australian exports")

Why is there no estimates at the ends?

  • for a 3-MA, there cannot be estimates at time 1 or time T because the observations at time 0 and T+1 are not available
  • Generally: there cannot be estimates at times near the endpoints

The order of the MA

  • larger order means smoother, flatter curve
  • larger order means more points lost at ends
  • if order = lenght of season or cycle IT removes pattern
  • But so far odd orders?
  • with even orders, we need to take 2 moving averages, to center the data

If order==4: 1/4[yt-2 + yt-1 + yt + yt+1] or 1/4[yt-1 + yt + yt+1 + yt+2] There are 2 options to compute the same 4-MA so we need to recenter at half of both 4_MA : 1/2(1/4[yt-2 + yt-1 + yt + yt+1] + 1/4[yt-1 + yt + yt+1 + yt+2]) = 1/8yt-2 + 1/4yt-1 + 1/4yt + 1/4yt=1 + 1/8yt+2

A MA of the same lenght as the season removes the seasonal pattern

  • For quarterly data: use 2x4 MA
  • For monthly data: use a 2x12 MA

Moving average trend-cycle

us_retail_employment_1990 |>
  mutate(`12-MA` = slider::slide_dbl(Employed, mean,
                                     .before = 5, .after = 6, .complete = TRUE),
         `2x12-MA` = slider::slide_dbl(`12-MA`, mean,
                                        .before = 1, .after = 0, .complete = TRUE)) -> us_retail_employment_1990_ma

us_retail_employment_1990_ma

Plotting it

us_retail_employment_1990_ma |>
  autoplot(Employed, color="black") +
  autolayer(us_retail_employment_1990_ma, vars("2x12-MA"),
            color="blue") +
  labs(y="Persons (thousands)", title = "Total employment in US retail")

Classical Decomposition (estimating time series components)

Additive decomposition: components add

Multiplicative Decomposition: components multiply

  1. EStimate Tt using MA (2xm)-MA if m is even. Otherwise, estimate Tt using m-MA

  2. Compute de-trended series

  1. De-trending

Remove smoothed series Tt from yt to leave St and Rt

This will leave behind Seasonal and Remainder

  1. Estimate Seasonal component

If necessary CONSTRAIN adjust the seasonal indices so that:

The seasonal component St simply consists of replications of the seasonal indices

Classical decomposition

Additive

dcmp <- us_retail_employment_1990 |>
  model(stl = STL(Employed))
components(dcmp) |> autoplot() +
  labs(title = "Classical additive decomposition of US retail employment")

Multiplicative

dcmp <- us_retail_employment_1990 |>
  model(classical_decomposition(Employed, type = "multiplicative"))
components(dcmp) |> autoplot() +
  labs(title = "Classical multiplicative decomposition of US retail employment")

General comments of Classical Decomposition

  • Estimate of trend is unavailable for first few and last few observations
  • Seasonal component repeats form year to year. May not be realistic
  • Not robust to outliers
  • Newer methods desinged to overcome these problems

Methods used in official statistics

Timeseries decomposition is officially used in official statistics bureaus

National Statistics Offices

How to do it using the FABLE packages

Install packages

# install.packages("seasonal")

Load packages

# library(seasonal)

X-13-ARIMA-SEATS Decomposition

x11_dcmp <- us_retail_employment_1990 |>
  model(x11 = X_13ARIMA_SEATS(Employed ~ x11()))

components(x11_dcmp) # show the components

Plotting X-13-ARIMA MULTIPLICATIVE

components(x11_dcmp) |> autoplot()

Advantages

  • Relatively robust to outliers
  • Completly automated choices for trend and seasonal changes
  • Very widely tested on economic data over a long period of time

Disadvantages

  • No prediction/confidence intervals
  • Ad hoc method with no underlying model
  • ONLY DEVELOPED FOR MONTHLY/QUARTER DATA

Extensions: X-12ARIMA- and X-13ARIMA

  • The X11, X12ARIMA, X13ARIMA methods are based on Census II decomposition
  • These allow adjustments for trading days and other explanatory variables
  • Known outliers can be ommited
  • Level shifts and ramp effects can be modelled
  • Missing values estimated and replaced
  • Holiday factors can be estimated

X-13ARIMA-SEATS

seats_dcmp <- us_retail_employment_1990 |>
  model(seats = X_13ARIMA_SEATS(Employed ~ seats()))

components(seats_dcmp) |> autoplot()

Advantages of X-13ARIMA-SEATS

  • Model based
  • CAN GET CONFIDENCE INTERVALS
  • Smooth trend estimate
  • Allows estimates at end points
  • Allows changing seasonality
  • Developed for economic data

Disadvantages of X-13ARIMA-SEATS

  • ONLY DEVELOPED FOR Q AND M DATA

STL Seasonal and Trend decomposition using Loess

Disadvantages:

For multiplicative: Take logs to get multiplicative decomposition Use Box-Cox transformations to get other decompositions

Example

us_retail_employment_1990 |>
  model(STL(Employed)) |>
  components() |>
  autoplot()

We can control the change in seasonal pattern and trend with windows

us_retail_employment_1990 |>
  model(STL(Employed ~ season(window=15) + trend(window=6665))) |>
  components() |>
  autoplot()

Observations

  • Lot of structure in the remainder
  • Because trend window = 6665
  • Trend becomes a straight line
  • What should have gone in trend goes to remainder
  • Default params work pretty good

Trend with small window

us_retail_employment_1990 |>
  model(STL(Employed ~ season(window=15) + trend(window=15))) |>
  components() |>
  autoplot()

  • Observations

  • With trend window = 15

  • We capture the interesting behaviour our in Trend

  • Noise goes to remainder

  • No strong structure in remainder

Robust argument (default is false)

us_retail_employment_1990 |>
  model(STL(Employed ~ season(window=15) + trend(window=15), robust = TRUE)) |>
  components() |>
  autoplot()

Summary

  • trend(window = ?) controls wiggliness of trend component
    • Because of Loes
    • weighted least squared fits
    • smaller windows, fewer obs for each local linear regression
  • season(window = ?) controls variations on seasonal component
    • Same component in different years
    • How many years to use when estimating each of seasonal component
  • season(window = ‘periodic’) is equivalent to infinite window

By default

  • chooses season(window=13)
  • Can include transformations

STL DECOMPOSITION

  • Default trend: window = nextodd(ceiling((1.5*period)/(1-(1.5/s.window))))
  • Default season: window = 13

How does it work?

  • Updated the trend and seasonal component iteratively
  • Starts off by saying, lets suppose there is no trend, estimate seasonality by classical decomp
  • Removes that, tries to estimate trend using LOES curve
  • Iterates

Explanation:

  • Starts with Tt = 0
  • Uses a mixture of loess and MA to sucessively refine the trend and seasonal estimates
  • The trend window controls loess bandwidth applied to deasonalised values
  • The season window controls loess bandwidth applied to detrended subseries
  • Robustbess weights based on remainder

THE END

LS0tCnRpdGxlOiAiVGltZSBTZXJpZXMgRGVjb21wb3NpdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KGZwcDMpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KEdHYWxseSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoc2Vhc29uYWwpCmBgYAoKIyMjIFRyYW5zZm9ybWF0aW9ucyBhbmQgYWRqdXN0bWVudHMKCiogUGVyIGNhcGl0YSBhZGp1c3RtZW50czogRGl2aWRpbmcgZGF0YSBieSBwb3B1bGF0aW9uCgoKYGBge3J9Cmdsb2JhbF9lY29ub215IHw+CiAgZmlsdGVyKENvdW50cnkgPT0gIkF1c3RyYWxpYSIpIHw+CiAgYXV0b3Bsb3QoR0RQIC8gUG9wdWxhdGlvbikKYGBgCgojIyMgSW5mbGF0aW9uIGFkanVzdG1lbnRzCgpBdXN0cmFsaWFuIG5ld3NwYXBlcnMgYWRqdXN0ZWQgZm9yIGlubGZhdGlvbgoKYGBge3J9CiMjIFJldGFpbCBDUEkKYXVzX2RhdGFzZXQgPC0gYXVzX3JldGFpbCB8PgogIGZpbHRlcihJbmR1c3RyeSA9PSAiTmV3c3BhcGVyIGFuZCBib29rIHJldGFpbGluZyIpIHw+CiAgZ3JvdXBfYnkoSW5kdXN0cnkpIHw+CiAgaW5kZXhfYnkoWWVhciA9IHllYXIoTW9udGgpKSB8PgogIHN1bW1hcmlzZShUdXJub3ZlciA9IHN1bShUdXJub3ZlcikpCgphdXNfZGF0YXNldCB8PiBhdXRvcGxvdCgpCmBgYAoqIFdlIHNlZSB0aGUgZGF0YSBnb2luZyB1cCB1bnRpbGwgYXJvdW5kIDIwMTAgYW5kIHRoZW4gc3RhcnQgZGlwcGluZyBkb3duLgoqIElzIHRoaXMgYmVjYXVzZSBhIGNoYW5nZSBpbiBpbmR1c3RyeSBvciBqdXN0IGEgY2hhbmdlIGluIHRoZSBDUEk/CiogT3IgaXMgaXQgYmVjYXVzZSB0aGUgQ1BJPwoKYGBge3J9CiMjIENQSQphdXNfZWNvbm9teSA8LSBnbG9iYWxfZWNvbm9teSB8PgogIGZpbHRlcihDb2RlID09ICJBVVMiKQoKYXVzX2Vjb25vbXkKYGBgCgpKb2luaW5nIHRoZSBkYXRhc2V0cwoKYGBge3J9CmF1c19kYXRhc2V0IHw+CiAgbGVmdF9qb2luKGF1c19lY29ub215LCBieT0iWWVhciIpIHw+CiAgbXV0YXRlKEFkanVzdGVkX1R1cm5vdmVyID0gVHVybm92ZXIgLyBDUEkgKjEwMCkgfD4KICBwaXZvdF9sb25nZXIoYyhUdXJub3ZlciwgQWRqdXN0ZWRfVHVybm92ZXIpLCB2YWx1ZXNfdG8gPSAiVHVybm92ZXIiKSB8PgogIG11dGF0ZShuYW1lPWZhY3RvcihuYW1lLCBsZXZlbHMgPSBjKCJUdXJub3ZlciIsIkFkanVzdGVkX3R1cm5vdmVyIikpKSB8PgogIGdncGxvdChhZXMoeD1ZZWFyLHk9VHVybm92ZXIpKSArIGdlb21fbGluZSgpICsKICBmYWNldF9ncmlkKG5hbWUgfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnModGl0bGU9IlR1cm5vdmVyOiBBdXN0cmFsaWFuIHByaW50IG1lZGlhIGluZHVzdHJ5IiwgeT0iJEFVIikKYGBgCgoqIFdlIGNhbiBzZWUgdGhhdCBhZGp1c3RlZCBmb3IgQ1BJIHRoZSBUdXJub3ZlciBoYXMgYmVlbiBkZWNsaW5pbmcgc2luY2UgMTk4MCdzCiogRGVjbGluZSBzaW5jZSAyMDEwIGlzIGJlZW4gZmFzdGVyLCBidXQgaGFzIGJlZW4gZ29pbmcgb24gc2luY2UgMTk4MAoKYFdoZW4gZm9yZWNhc3RpbmcgeW91IHdhbnQgdG8gZm9yZWNhc3QgdGFraW5nIGluIHRvIGFjY291bnQgQ1BJYAoKIyMjIE1hdGhlbWF0aWNhbCBUcmFuc2Zvcm1hdGlvbgoKKiBUYWtlIGRhdGEgYW5kIHRyYW5zZm9ybSBieSBhcHBseWluZyBhIG1hdGhlbWF0aWNhbCB0cmFuc2Zvcm1hdGlvbgoqIFdlIHRyeSB0byBtYWtlIHRoZSB2YXJpYXRpb25zIGluIHRoZSBzZXJpZXMgbW9yZSBIT01PR0VORU9VUwoKYGBge3J9CmZvb2QgPC0gYXVzX3JldGFpbCB8PgogIGZpbHRlcihJbmR1c3RyeSA9PSAiRm9vZCByZXRhaWxpbmciKSB8PgogIHN1bW1hcmlzZShUdXJub3ZlciA9IHN1bShUdXJub3ZlcikpCmZvb2QKYGBgCgojIyMgVGhlIG9yaWdpbmFsIHBsb3Q6CgpgYGB7cn0KZm9vZCB8PiBhdXRvcGxvdChUdXJub3ZlcikKYGBgCgojIyMgU3F1YXJlIHJvb3Q6CgpXb3JrcywgYnV0IHZhcmlhdGlvbnMgYXQgdGhlIHN0YXJ0IG9mIHNlcmllcyBhcmUgc3RpbGwgYmlnZ2VyIHRoYW4gdmFyaWF0aW9ucyBhdCB0aGUgZW5kCgpgYGB7cn0KZm9vZCB8PiBhdXRvcGxvdChzcXJ0KFR1cm5vdmVyKSkgKyBsYWJzKHRpdGxlID0gIlNxdWFyZSByb290IFR1cm5vdmVyIikKYGBgCgojIyMgQ3ViZSByb290CgpXb3JrcywgYnV0IHZhcmlhdGlvbnMgYXQgdGhlIHN0YXJ0IG9mIHNlcmllcyBhcmUgc3RpbGwgYmlnZ2VyIHRoYW4gdmFyaWF0aW9ucyBhdCB0aGUgZW5kCgpgYGB7cn0KZm9vZCB8PiBhdXRvcGxvdChUdXJub3Zlcl4oMS8zKSkgKyBsYWJzKHRpdGxlID0gIkN1YmUgcm9vdCBUdXJub3ZlciIpCmBgYAoKIyMjIExvZwoKYGBge3J9CmZvb2QgfD4gYXV0b3Bsb3QobG9nKFR1cm5vdmVyKSkgKyBsYWJzKHRpdGxlID0gIkxvZyBUdXJub3ZlciIpCmBgYAoKKiBNdWNoIGJldHRlciwgdmFyaWF0aW9ucyBhcmUgbW9yZSBIT01PR0VORU9VUwoqIFNpemUgb2YgZmx1Y3R1YXRpb25zIGF0IHRoZSBzdGFydCBhcmUgYWJvdXQgc2FtZSBzaXplIGF0IGVuZAoKVGhpcyBpcyBnb2luZyB0byBtYWtlIG1vZGVsaW5nIG11Y2ggZWFzaWVyCk1ha2VzIHRpbWVzZXJpZXMgZGVjb21wb3NpdGlvbnMgZWFzaWVyCkdvb2QgZmlyc3Qgc3RlcCB0byByZW1vdmUgdGhhdCBleHRyYSBoZXRlcm9nZW5laXR5CkFsbG93cyB1cyB0byBmaXQgYSBtdWNoIHNpbXBsZXIgbW9kZWwKCiMjIyBJbnZlcnNlIHRyYW5zZm9ybWF0aW9uCgpgYGB7cn0KZm9vZCB8PiBhdXRvcGxvdCgtMS9UdXJub3ZlcikgKyBsYWJzKHRpdGxlID0gIi0xL1R1cm5vdmVyIikKYGBgCgoqIEdvbmUgdG9vIGZhciwgTG9nIHdhcyBhYm91dCByaWdodAoKIyMjIEJPWC1DT1ggVHJhbnNmb3JtYXRpb25zCgpCaWNrZWwtRG9rc3VtIHRyYW5zZm9ybWF0aW9ucwoKIyMjIEZ1bmN0aW9uIHRvIGNvbXB1dGUgdGhlIG9wdGltYWwgbGFtYmRhCgpgYGB7cn0KZm9vZCB8PiBmZWF0dXJlcyhUdXJub3ZlciwgZmVhdHVyZXMgPSBndWVycmVybykKYGBgClRoaXMgYXR0ZW1wdHMgdG8gYmFsYW5jZSB0aGUgc2Vhc29uYWwgZmx1Y3R1YXRpb25zIGFuZCByYW5kb20gdmFyaWF0aW9uIGFjcm9zcyB0aGUgc2VyaWVzCkFsd2F5cyBjaGVjayB0aGUgcmVzdWx0cwpBIGxvdyB2YWx1ZSBvZiBsYW1iZGEgY2FuIGdpdmUgZXh0cmVtZWx5IGxhcmdlIHByZWRpY3Rpb24gaW50ZXJ2YWxzCgoKIyMjIFVzaW5nIHRoZSBndWVycmVybyB0cmFuc2Zvcm1hdGlvbgoKYGBge3J9CmZvb2QgfD4gYXV0b3Bsb3QoYm94X2NveChUdXJub3ZlciwgMC4wODk1MjY5NikpICsKICBsYWJzKHRpdGxlID0gIkJveC1Db3ggVHJhbnNmb3JtZWQgdHVybm92ZXIiKQpgYGAKCiogT2Z0ZW4gbm8gdHJhbnNmb3JtYXRpb25zIG5lZWRlZAoqIFNpbXBsZSB0cmFuc2Zvcm1hdGlvbnMgYXJlIGVhc2llciB0byBleHBsYWluIGFuZCB3b3JrIHdlbGwgZW5vdWdoCiogQ2FuIGhhdmUgYSBWRVJZIExBUkdFIGVmZmVjdCBvbiBQcmVkaWN0aW9uIEludGVydmFsCiogSWYgc29tZSBkYXRhIGFyZSAwIG9yIG5lZ2F0aXZlLCB0aGVuIHVzZSBsYW1iZGEgPiAwCiogbG9nMXAoKSBjYW4gYWxzbyBiZSB1c2VmdWwgZm9yIGRhdGEgd2l0aCAwcwoqIENob29zaW5nIGxvZ3MgaXMgYSBzaW1wbGUgd2F5IHRvIGZvcmNlIGZvcmVjYXN0cyB0byBiZSBwb3NpdGl2ZQoqIFRyYW5zZm9ybWF0aW9ucyBtdXN0IGJlIHJldmVyc2VkIHRvIG9idGFpbiBmb3JlY2FzdHMgb24gdGhlIG9yaWdpbmFsIHNjYWxlLiAoSGFuZGxlZCBhdXRvbWF0aWNhbGx5IGJ5IGZhYmxlKQoKIyBUaW1lIHNlcmllcyBjb21wb25lbnRzCgpBIHRpbWUgc2VyaWVzIGRlY29tcG9zaXRpb24gaW52b2x2ZXMgZXN0aW1hdGluZyB0aW1lc2VyaWVzIGNvbXBvbmVudHMKCiogVHJlbmQ6IGxvbmcgdGVybSBpbmNyZWFzZSBvciBkZWNyZWFzZQoqIFNlYXNvbmFsIHBhdHRlcm46IGluZmx1ZW5jZWQgYnkgdGhlIGNhbGVuZGFyCiogQ3ljbGVzOiByaXNlcyBvciBmYWxscyB0aGF0IGFyZSBub3Qgb2YgZml4ZWQgcGVyaW9kCgpEZWNvbXBvc2l0aW9uOiB5dCA9IGYoU3QsIFR0LCBSdCkgd2hlcmU6CiogeXQgPSBkYXRhIGF0IHBlcmlvZCB0CiogU3QgPSBzZWFzb25hbCBjb21wb25lbnQgYXQgcGVyaW9kIHQKKiBUdCA9IHRyZW5kIGF0IHBlcmlvZCB0CiogUnQgPSByZW1haW5kZXIgY29tcG9uZW50IGF0IHBlcmlvZCB0CgpBZGRpdGl2ZSBkZWNvbXBvc2l0aW9uOiB5dCA9IFN0ICsgVHQgKyBSdApNdWx0aXBsaWNhdGl2ZSBkZWNvbXBvc2l0aW9uOiB5dCA9IFN0ICogVHQgKiBSdAoKIyMjIEFkZGl0aXZlIG1vZGVsCgoqIEFwcHJvcGlhdGUgaWYgbWFnbml0dWRlIG9mIHNlYXNvbmFsIGZsdWN0dWF0aW9ucyBkb2VzIG5vdCB2YXJ5IHdpdGggbGV2ZWwKKiBJZiBzZWFzb25hbCBwYXR0ZXJucyBhcmUgcHJvcG9ydGlvbmFsIHRvIHRoZSBsZXZlbCBvZiB0aGUgc2VyaWVzIC0+IE1VTFRJUExJQ0FUSVZFIE1PREVMCiogTXVsdGlwbGljYXRpdmUgZGVjb21wb3NpdGlvbiBtb3JlIHByZXZhbGVudCB3aXRoIGVjb25vbWljIHNlcmllcwoKKiBgQUxURVJOQVRJVkVgOiBVc2UgYSBCb3gtQ294IHRyYW5zZm9ybWF0aW9uLCBhbmQgdGhlbiB1c2UgYWRkaXRpdmUgZGVjb21wb3NpdGlvbgoqIExvZ3MgdHVybiBtdWx0aXBsaWNhdGl2ZSByZWxhdGlvbnNoaXBzIGluIHRvIGFkZGl0aXZlIHJlbGF0aW9uc2hpcHMKCmB5dCA9IFN0ICogVHQgKiBSdCAtLT4gbG4oeXQpID0gbG4oU3QpICsgbG4oVHQpICsgbG4oUnQgKWAKCiMjIyBFeGFtcGxlIAoKYGBge3J9CnVzX2VtcGxveW1lbnQgfD4gCiAgZmlsdGVyKFRpdGxlID09ICJSZXRhaWwgVHJhZGUiLCB5ZWFyKE1vbnRoKSA+PSAxOTkwKSB8PgogIHNlbGVjdCgtU2VyaWVzX0lEKSAtPiB1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwCgp1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwIHw+IGF1dG9wbG90KEVtcGxveWVkKSArIGdlb21fcG9pbnQoKSArCiAgbGFicyh5PSJQZXJzb25zICh0aG91c2FuZHMpIiwgdGl0bGUgPSAiVG90YWwgZW1wbG95bWVudCBpbiBVUyByZXRhaWwiKQpgYGAKCiogV2UgaGF2ZSBUcmVuZCArIEN5Y2xlICsgU2Vhc29uYWxpdHksIGxldHMgZml0IGEgbW9kZWwKCmBgYHtyfQp1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwIHw+CiAgbW9kZWwoc3RsID0gU1RMKEVtcGxveWVkKSkKYGBgCgojIyMgTG9va2luZyBhdCB0aGUgbW9kZWwgY29tcG9uZW50cwoKYGBge3J9CmRjbXAgPC0gdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MCB8PgogIG1vZGVsKHN0bCA9IFNUTChFbXBsb3llZCkpCmNvbXBvbmVudHMoZGNtcCkKYGBgCgojIyMgQXV0b3Bsb3QgdGhlIGNvbXBvbmVudHMKCmBgYHtyfQpjb21wb25lbnRzKGRjbXApIHw+IGF1dG9wbG90KCkKYGBgCgojIyMgT3RoZXIgdGhpbmdzIHdlIGNhbiBkbyB3aXRoIHRoZSBjb21wb25lbnRzCgpgYGB7cn0KdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MCB8PgogIGF1dG9wbG90KEVtcGxveWVkLCBjb2xvcj0iYmxhY2siKSArCiAgYXV0b2xheWVyKGNvbXBvbmVudHMoZGNtcCksIHRyZW5kLCBjb2xvcj0iI0Q1NUUwMCIpICsKICBsYWJzKHk9ICJQZXJzb25zICh0aG91c2FuZHMpIiwgdGl0bGUgPSAiVG90YWwgZW1wbG95bWVudCBpbiBVUyByZXRhaWwiKQpgYGAKCiMjIyBTdWJzZXJpZXMgcGxvdCBgVXNlZnVsIGlmIGludGVyZXN0ZWQgaW4gU0hBUEUgb2Ygc2Vhc29uYWxpdHlgCgpgYGB7cn0KY29tcG9uZW50cyhkY21wKSB8PiBnZ19zdWJzZXJpZXMoc2Vhc29uX3llYXIpCmBgYAoKIyMjIFNlc29uYWwgQWRqdXN0bWVudAoKKiBVc2VmdWwgYnktcHJvZHVjdCBvZiBkZWNvbXBvc2l0aW9uOiBhbiBlYXN5IHdheSB0byBjYWxjdWxhdGUgc2Vhc29uYWxseSBhZGp1c3RlZCBkYXRhCiogQWRkaXRpdmUgZGVjb21wb3NpdGlvbjogc2Vhc29uYWxseSBhZGp1c3RlZCBkYXRhIGdpdmVuIGJ5IHl0IC0gU3QgPSBUdCArIFJ0CiogTXVsdGlwbGljYXRpdmU6IHl0L1N0ID0gVHQgKiBSdAoKUGxvdHRpbmcgdGhlIHNlYXNvbmFsbHkgYWRqdXN0ZWQgZGF0YTogeXQgLSBTdCA9IFR0ICsgUnQKCgpgYGB7cn0KdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MCB8PgogIGF1dG9wbG90KEVtcGxveWVkLCBjb2xvcj0iYmxhY2siKSArCiAgYXV0b2xheWVyKGNvbXBvbmVudHMoZGNtcCksIHNlYXNvbl9hZGp1c3QsIGNvbG9yPSJibHVlIikgKwogIGxhYnMoeT0iUGVyc29ucyAodGhvdXNhbmRzKSIsIHRpdGxlID0gIlRvdGFsIGVtcGxveW1lbnQgaW4gVVMgcmV0YWlsIikKYGBgCgpPYnNlcnZhdGlvbnM6CgoqIFdlIGNhbiBzZWUgdGhlIHNlYXNvbl9hZGp1c3QgKGJsdWUpIGxpbmUgaXMgbW9yZSB3b2JibHkgdGhhbiB0aGUgdHJlbmQgbGluZSBpbiB0aGUgYWJvdmUgY2hhcnQKKiBUaGlzIGlzIGR1ZSB0byB0aGUgZWZmZWN0IG9mIFJ0IHRoZSByZW1haW5kZXIsIHdoaWNoIGlzIGFsbW9zdCBhbHdheXMgbm9pc3kgaW4gbmF0dXJlCgojIyMgSG93IHRvIHNlYXNvbmFsIGFkanVzdDoKCiogV2UgdXNlIGVzdGltYXRlcyBvZiBTIGJhc2VkIG9uIHBhc3QgdmFsdWVzIHRvIHNlYXNvbmFsbHkgYWRqdXN0IGEgY3VycmVudCB2YWx1ZQoqIFNlYXNvbmFsbHkgYWRqdXN0ZWQgc2VyaWVzIHJlZmxlY3QgKipyZW1haW5kZXJzKiogYXMgd2VsbCBhcyAqKnRyZW5kKioKKiBUaGVyZWZvcmUgdGhleSBhcmUgYG5vdGAgYHNtb290aGAsIGFuZCBgZG93bnR1cm5zYCBvciBgdXB0dXJuc2AgY2FuIGJlIGBtaXNsZWFkaW5nYAoqIGBJdCBpcyBiZXR0ZXIgdG8gdXNlIHRoZSB0cmVuZC1jeWNsZSBjb21wb25lbnQgdG8gbG9vayBmb3IgdHVybmluZyBwb2ludHNgCgojIE1vdmluZyBBdmVyYWdlcyAoQ2xhc3NpY2FsIERlY29tcG9zaXRpb24pCgoqIFRyYWRpdGlvbmFsIHdheSB0byBwZXJmb3JtIHRpbWUgc2VyaWVzIGRlY29tcG9zaXRpb25zLCAKKiBPcmlnaW5hdGVkIGluIHRoZSAxOTIwJ3MgYnV0IHN0aWxsIHRoZSBiYXNpcyBmb3IgbWFueSBkZWNvbXBvc2l0aW9ucy4KCkEgbW92aW5nIGF2ZXJhZ2UgaXMgYW4gYXZlcmFnZSBvZiBuZWFyYnkgcG9pbnRzCgoqIG9ic2VydmF0aW9ucyBuZWFyYnkgaW4gdGltZSBhcmUgYWxzbyBsaWtlbHkgdG8gYmUgYGNsb3NlIGluIHZhbHVlYAoqIGF2ZXJhZ2UgZWxpbWluYXRlcyBzb21lIGByYW5kb21uZXNzYCBpbiB0aGUgZGF0YSwgbGVhdmluZyBhIHNtb290aCB0cmVuZC1jeWNsZSBjb21wb25lbnQKKiAzLU1BOiBUdCA9ICh5KHQtMSkgKyB5dCArIHkodCsxKSkvMwoqIDUtTUE6IFR0ID0gKHkodC0yKSArIHkodC0xKSArIHl0ICsgeSh0KzEpICsgeSh0KzIpKS81CiogZWFjaCBhdmVyYWdlIGNvbXB1dGVkIGJ5IGRyb3BwaW5nIGBvbGRlc3RgIG9ic2VydmF0aW9uIGFuZCBpbmNsdWRpbmcgYG5leHRgIG9ic2VydmF0aW9uCiogYXZlcmFnaW5nIGBtb3Zlc2AgdHJvdWdoIHRpbWUgc2VyaWVzIHVudGlsIHRyZW5kLWN5Y2xlIGNvbXB1dGVkIGF0IGVhY2ggb2JzZXJ2YXRpb24gcG9zc2libGUKCiMjIyBGaXJzdCBzdGVwOiBHZXQgYW5kIGVzdGltYXRlIG9mIHRoZSB0cmVuZCBjb21wb25lbnQKCiogV2UgdXNlIE1vdmluZyBBdmVyYWdlcyBmb3IgdGhpczoKCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgfD4KICBmaWx0ZXIoQ291bnRyeSA9PSAiQXVzdHJhbGlhIikgfD4KICBhdXRvcGxvdChFeHBvcnRzKSArCiAgbGFicyh5PSIlIG9mIEdEUCIsIHRpdGxlID0gIlRvdGFsIEF1c3RyYWxpYW4gZXhwb3J0cyIpCmBgYAoKCiMjIyBXaHkgaXMgdGhlcmUgbm8gZXN0aW1hdGVzIGF0IHRoZSBlbmRzPwoKKiBmb3IgYSAzLU1BLCB0aGVyZSBjYW5ub3QgYmUgZXN0aW1hdGVzIGF0IHRpbWUgMSBvciB0aW1lIFQgYmVjYXVzZSB0aGUgb2JzZXJ2YXRpb25zIGF0IHRpbWUgMCBhbmQgVCsxIGFyZSBub3QgYXZhaWxhYmxlCiogR2VuZXJhbGx5OiB0aGVyZSBjYW5ub3QgYmUgZXN0aW1hdGVzIGF0IHRpbWVzIG5lYXIgdGhlIGVuZHBvaW50cwoKIyMjIFRoZSBvcmRlciBvZiB0aGUgTUEKCiogbGFyZ2VyIG9yZGVyIG1lYW5zIHNtb290aGVyLCBmbGF0dGVyIGN1cnZlCiogbGFyZ2VyIG9yZGVyIG1lYW5zIG1vcmUgcG9pbnRzIGxvc3QgYXQgZW5kcwoqIGBpZiBvcmRlciA9IGxlbmdodCBvZiBzZWFzb24gb3IgY3ljbGVgIElUIGByZW1vdmVzIHBhdHRlcm5gCiogQnV0IHNvIGZhciBvZGQgb3JkZXJzPwoqIHdpdGggZXZlbiBvcmRlcnMsIHdlIG5lZWQgdG8gdGFrZSAyIG1vdmluZyBhdmVyYWdlcywgdG8gY2VudGVyIHRoZSBkYXRhCgpJZiBvcmRlcj09NDogMS80W3l0LTIgKyB5dC0xICsgeXQgKyB5dCsxXSBvciAxLzRbeXQtMSArIHl0ICsgeXQrMSArIHl0KzJdClRoZXJlIGFyZSAyIG9wdGlvbnMgdG8gY29tcHV0ZSB0aGUgc2FtZSA0LU1BIHNvIHdlIG5lZWQgdG8gcmVjZW50ZXIgYXQgaGFsZiBvZiBib3RoCjRfTUEgOiAxLzIoMS80W3l0LTIgKyB5dC0xICsgeXQgKyB5dCsxXSArIDEvNFt5dC0xICsgeXQgKyB5dCsxICsgeXQrMl0pID0gMS84eXQtMiArIDEvNHl0LTEgKyAxLzR5dCArIDEvNHl0PTEgKyAxLzh5dCsyCgpgQSBNQSBvZiB0aGUgc2FtZSBsZW5naHQgYXMgdGhlIHNlYXNvbiByZW1vdmVzIHRoZSBzZWFzb25hbCBwYXR0ZXJuYAoKKiBGb3IgcXVhcnRlcmx5IGRhdGE6IHVzZSAyeDQgTUEKKiBGb3IgbW9udGhseSBkYXRhOiB1c2UgYSAyeDEyIE1BCgojIyMgTW92aW5nIGF2ZXJhZ2UgdHJlbmQtY3ljbGUKCmBgYHtyfQp1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwIHw+CiAgbXV0YXRlKGAxMi1NQWAgPSBzbGlkZXI6OnNsaWRlX2RibChFbXBsb3llZCwgbWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5iZWZvcmUgPSA1LCAuYWZ0ZXIgPSA2LCAuY29tcGxldGUgPSBUUlVFKSwKICAgICAgICAgYDJ4MTItTUFgID0gc2xpZGVyOjpzbGlkZV9kYmwoYDEyLU1BYCwgbWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5iZWZvcmUgPSAxLCAuYWZ0ZXIgPSAwLCAuY29tcGxldGUgPSBUUlVFKSkgLT4gdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MF9tYQoKdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MF9tYQpgYGAKCiMjIyBQbG90dGluZyBpdAoKYGBge3J9CnVzX3JldGFpbF9lbXBsb3ltZW50XzE5OTBfbWEgfD4KICBhdXRvcGxvdChFbXBsb3llZCwgY29sb3I9ImJsYWNrIikgKwogIGF1dG9sYXllcih1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwX21hLCB2YXJzKCIyeDEyLU1BIiksCiAgICAgICAgICAgIGNvbG9yPSJibHVlIikgKwogIGxhYnMoeT0iUGVyc29ucyAodGhvdXNhbmRzKSIsIHRpdGxlID0gIlRvdGFsIGVtcGxveW1lbnQgaW4gVVMgcmV0YWlsIikKYGBgCgojIENsYXNzaWNhbCBEZWNvbXBvc2l0aW9uIChlc3RpbWF0aW5nIHRpbWUgc2VyaWVzIGNvbXBvbmVudHMpCgpBZGRpdGl2ZSBkZWNvbXBvc2l0aW9uOiBjb21wb25lbnRzIGFkZAoKKiB5dCA9IFR0ICsgU3QgKyBSdAoKTXVsdGlwbGljYXRpdmUgRGVjb21wb3NpdGlvbjogY29tcG9uZW50cyBtdWx0aXBseQoKKiB5dCA9IFR0ICogU3QgKiBSdAoKMS4gRVN0aW1hdGUgVHQgdXNpbmcgTUEgKDJ4bSktTUEgaWYgbSBpcyBldmVuLiBPdGhlcndpc2UsIGVzdGltYXRlIFR0IHVzaW5nIG0tTUEKCjIuIENvbXB1dGUgZGUtdHJlbmRlZCBzZXJpZXMKCiAgKiBBZGRpdGl2ZSBkZWNvbXBvc2l0aW9uOiB5dCAtIFR0CiAgKiBNdWx0aXBsaWNhdGl2ZSBkZWNvbXBvc2l0aW9uOiB5dC9UdAoKMy4gRGUtdHJlbmRpbmcKClJlbW92ZSBzbW9vdGhlZCBzZXJpZXMgVHQgZnJvbSB5dCB0byBsZWF2ZSBTdCBhbmQgUnQKCiogQWRkaXRpdmUgbW9kZWwgeXQgLSBUdCArIChUdCArIFN0ICsgUnQpIC0gU3QgPSBTdCArIFJ0CiogTXVsdGlwbGljYXRpdmUgbW9kZWw6IHl0L1R0ID0gVHR4U3R4UnQvVHQgPSBTdHhSdAoKVGhpcyB3aWxsIGxlYXZlIGJlaGluZCBTZWFzb25hbCBhbmQgUmVtYWluZGVyCgo0LiBFc3RpbWF0ZSBTZWFzb25hbCBjb21wb25lbnQKCiogU2Vhc29uYWwgaW5kZXggZm9yIGVhY2ggc2Vhc29uIGlzIGVzdGltYXRlZCBhcyBhbiBhdmVyYWdlIG9mIHRoZSBkZXRyZW5kZWQgc2VyaWVzIGZvciB0aGF0IHNlYXNvbiBvZiBzdWNjZXNzaXZlIHllYXJzCiogRS5nLiB0YWtlIGF2ZXJhZ2VzIGFjcm9zcyBhbGwgSmFudWFyaWVzIHRvIGdldCBTMSBpZiB5b3VyIGRhdGEgaXMgbW9udGhseQoKSWYgbmVjZXNzYXJ5IGBDT05TVFJBSU5gIGFkanVzdCB0aGUgc2Vhc29uYWwgaW5kaWNlcyBzbyB0aGF0OgoKICAqIEFkZGl0aXZlOiBTMSArIFMyICsgLi4uICsgU04gPSAwCiAgKiBNdWx0aXBsaWNhdGl2ZTogUzEgKyBTMiArIC4uLiArIFNOID0gbQogIApUaGUgc2Vhc29uYWwgY29tcG9uZW50IFN0IHNpbXBseSBjb25zaXN0cyBvZiByZXBsaWNhdGlvbnMgb2YgdGhlIHNlYXNvbmFsIGluZGljZXMKCiogQWRkaXRpdmUgZGVjb21wb3NpdGlvbjogUnQgPSB5dCAtIFR0IC0gU3QKKiBNdWx0aXBsaWNhdGl2ZSBkZWNvbXBvc2l0aW9uOiBSdCA9IHl0LyhUdCpTdCkKCmBDbGFzc2ljYWwgZGVjb21wb3NpdGlvbmAKCiogQ2hvb3NlIGFkZGl0aXZlIG9yIG11bHRpcGxpY2F0aXZlIGRlcGVuZGluZyBvbiB3aGljaCBnaXZlcyB0aGUgbW9zdCBzdGFibGUgY29tcG9uZW50cwoqIEZvciBtdWx0aXBsaWNhdGl2ZSBtb2RlbCwgdGhpcyBtZXRob2QgYS5rLmEuIGByYXRpby10by1tb3ZpbmctYXZlcmFnZWAgbWV0aG9kCgojIyMgQWRkaXRpdmUKCmBgYHtyfQpkY21wIDwtIHVzX3JldGFpbF9lbXBsb3ltZW50XzE5OTAgfD4KICBtb2RlbChzdGwgPSBTVEwoRW1wbG95ZWQpKQpjb21wb25lbnRzKGRjbXApIHw+IGF1dG9wbG90KCkgKwogIGxhYnModGl0bGUgPSAiQ2xhc3NpY2FsIGFkZGl0aXZlIGRlY29tcG9zaXRpb24gb2YgVVMgcmV0YWlsIGVtcGxveW1lbnQiKQpgYGAKCiMjIyBNdWx0aXBsaWNhdGl2ZQoKYGBge3J9CmRjbXAgPC0gdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MCB8PgogIG1vZGVsKGNsYXNzaWNhbF9kZWNvbXBvc2l0aW9uKEVtcGxveWVkLCB0eXBlID0gIm11bHRpcGxpY2F0aXZlIikpCmNvbXBvbmVudHMoZGNtcCkgfD4gYXV0b3Bsb3QoKSArCiAgbGFicyh0aXRsZSA9ICJDbGFzc2ljYWwgbXVsdGlwbGljYXRpdmUgZGVjb21wb3NpdGlvbiBvZiBVUyByZXRhaWwgZW1wbG95bWVudCIpCmBgYAoKIyMjIEdlbmVyYWwgY29tbWVudHMgb2YgQ2xhc3NpY2FsIERlY29tcG9zaXRpb24KCiogRXN0aW1hdGUgb2YgdHJlbmQgaXMgdW5hdmFpbGFibGUgZm9yIGZpcnN0IGZldyBhbmQgbGFzdCBmZXcgb2JzZXJ2YXRpb25zCiogKipTZWFzb25hbCBjb21wb25lbnQgcmVwZWF0cyoqIGZvcm0geWVhciB0byB5ZWFyLiBNYXkgbm90IGJlIHJlYWxpc3RpYwoqICoqTm90IHJvYnVzdCB0byBvdXRsaWVycyoqCiogTmV3ZXIgbWV0aG9kcyBkZXNpbmdlZCB0byBvdmVyY29tZSB0aGVzZSBwcm9ibGVtcwogIAojIGBNZXRob2RzIHVzZWQgaW4gb2ZmaWNpYWwgc3RhdGlzdGljc2AKClRpbWVzZXJpZXMgZGVjb21wb3NpdGlvbiBpcyBvZmZpY2lhbGx5IHVzZWQgaW4gb2ZmaWNpYWwgc3RhdGlzdGljcyBidXJlYXVzCgoqIENsYXNzaWNhbCBtZXRob2Qgb3JpZ2luYXRlZCBpbiAxOTIwCiogYENlbnN1cyBJSWAgbWV0aG9kIGludHJvZHVjZWQgaW4gYDE5NTdgCiogQmFzaXMgZm9yIGBYLTExYCBtZXRob2QgYW5kIHZhcmlhbnRzIChpbmNsdWRpbmcgWC0xMiwgWC0xMi1BUklNQSwgWC0xMy1BUklNQSkKKiBgU1RMYCBtZXRob2QgaW50cm9kdWNlZCBpbiBgMTk4M2AgYXQgQkVMTCBMQUJTCiogYFRSQU1PL1NFQVRTYCBpbnRyb2R1Y2VkIGluIGAxOTkwc2AgaW4gRXVyb3BlIChiYW5rIG9mIFNwYWluKQoKTmF0aW9uYWwgU3RhdGlzdGljcyBPZmZpY2VzCgoqIEFCUyAoQXVzdHJhbGlhKSB1c2VzIFgtMTItQVJJTUEKKiBVUyBDZW5zdXMgQnVyZWF1IHVzZXMgWC0xM0FSSU1BLVNFQVRTCiogU3RhdGlzdGljcyBDYW5hZGEgdXNlcyBYLTEyLUFSSU1BCiogT05TIChVSykgdXNlcyBYLTEyLUFSSU1BCiogRXVyb1N0YXQgdXNlcyBYLTEzQVJJTUEtU0VBVFMKCiMjIEhvdyB0byBkbyBpdCB1c2luZyB0aGUgYEZBQkxFYCBwYWNrYWdlcwoKIyMjIEluc3RhbGwgcGFja2FnZXMKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoInNlYXNvbmFsIikKYGBgCiMjIyBMb2FkIHBhY2thZ2VzCgpgYGB7cn0KIyBsaWJyYXJ5KHNlYXNvbmFsKQpgYGAKCiMjIyBYLTEzLUFSSU1BLVNFQVRTIERlY29tcG9zaXRpb24KCmBgYHtyfQp4MTFfZGNtcCA8LSB1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwIHw+CiAgbW9kZWwoeDExID0gWF8xM0FSSU1BX1NFQVRTKEVtcGxveWVkIH4geDExKCkpKQoKY29tcG9uZW50cyh4MTFfZGNtcCkgIyBzaG93IHRoZSBjb21wb25lbnRzCmBgYAoKIyMjIFBsb3R0aW5nIFgtMTMtQVJJTUEgYE1VTFRJUExJQ0FUSVZFYAoKYGBge3J9CmNvbXBvbmVudHMoeDExX2RjbXApIHw+IGF1dG9wbG90KCkKYGBgCgojIyMgQWR2YW50YWdlcwoKKiBSZWxhdGl2ZWx5IHJvYnVzdCB0byBvdXRsaWVycwoqIENvbXBsZXRseSBhdXRvbWF0ZWQgY2hvaWNlcyBmb3IgdHJlbmQgYW5kIHNlYXNvbmFsIGNoYW5nZXMKKiBWZXJ5IHdpZGVseSB0ZXN0ZWQgb24gZWNvbm9taWMgZGF0YSBvdmVyIGEgbG9uZyBwZXJpb2Qgb2YgdGltZQoKIyMjIERpc2FkdmFudGFnZXMKCiogTm8gcHJlZGljdGlvbi9jb25maWRlbmNlIGludGVydmFscwoqIEFkIGhvYyBtZXRob2Qgd2l0aCBubyB1bmRlcmx5aW5nIG1vZGVsCiogYE9OTFkgREVWRUxPUEVEIEZPUiBNT05USExZL1FVQVJURVIgREFUQWAKCiMjIyBFeHRlbnNpb25zOiBYLTEyQVJJTUEtIGFuZCBYLTEzQVJJTUEKCiogVGhlIFgxMSwgWDEyQVJJTUEsIFgxM0FSSU1BIG1ldGhvZHMgYXJlIGJhc2VkIG9uIENlbnN1cyBJSSBkZWNvbXBvc2l0aW9uCiogVGhlc2UgYWxsb3cgYWRqdXN0bWVudHMgZm9yIHRyYWRpbmcgZGF5cyBhbmQgb3RoZXIgZXhwbGFuYXRvcnkgdmFyaWFibGVzCiogS25vd24gb3V0bGllcnMgY2FuIGJlIG9tbWl0ZWQKKiBMZXZlbCBzaGlmdHMgYW5kIHJhbXAgZWZmZWN0cyBjYW4gYmUgbW9kZWxsZWQKKiBNaXNzaW5nIHZhbHVlcyBlc3RpbWF0ZWQgYW5kIHJlcGxhY2VkCiogSG9saWRheSBmYWN0b3JzIGNhbiBiZSBlc3RpbWF0ZWQKCiMjIFgtMTNBUklNQS1TRUFUUwoKYGBge3J9CnNlYXRzX2RjbXAgPC0gdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MCB8PgogIG1vZGVsKHNlYXRzID0gWF8xM0FSSU1BX1NFQVRTKEVtcGxveWVkIH4gc2VhdHMoKSkpCgpjb21wb25lbnRzKHNlYXRzX2RjbXApIHw+IGF1dG9wbG90KCkKYGBgCgojIyMgQWR2YW50YWdlcyBvZiBgWC0xM0FSSU1BLVNFQVRTYAoKKiBNb2RlbCBiYXNlZAoqIGBDQU4gR0VUIENPTkZJREVOQ0UgSU5URVJWQUxTYAoqIFNtb290aCB0cmVuZCBlc3RpbWF0ZQoqIEFsbG93cyBlc3RpbWF0ZXMgYXQgZW5kIHBvaW50cwoqIEFsbG93cyBjaGFuZ2luZyBzZWFzb25hbGl0eQoqIERldmVsb3BlZCBmb3IgZWNvbm9taWMgZGF0YQoKIyMjIERpc2FkdmFudGFnZXMgb2YgYFgtMTNBUklNQS1TRUFUU2AKCiogYE9OTFkgREVWRUxPUEVEIEZPUiBRIEFORCBNIERBVEFgCgojIFNUTCBTZWFzb25hbCBhbmQgVHJlbmQgZGVjb21wb3NpdGlvbiB1c2luZyBMb2VzcwoKKiBMb2VzcyBpcyBhIGZvcm0gb2Ygc21vb3RoaW5nIGJhc2VkIG9uIGxvY2FsbHkgbGluZWFyIHJlZ3Jlc3Npb25zCiogVmVyeSB2ZXJzYXRpbGUgYW5kIHJvYnVzdAoqIFdpbGwgaGFuZGxlIGFueSB0eXBlIG9mIHNlYXNvbmFsaXR5CiogU2Vhc29uYWwgY29tcG9uZW50IGFsbG93ZWQgdG8gY2hhbmdlIG92ZXIgdGltZQogICogUmF0ZSBvZiBjaGFuZ2UgY29udHJvbGxlZCBieSB1c2VyCiogU21vb3RobmVzcyBvZiB0cmVuZC1jeWNsZSBhbHNvIGNvbnRyb2xsZWQgYnkgdXNlcgoqIFJvYnVzdCB0byBvdXRsaWVycwoKRGlzYWR2YW50YWdlczoKCiogTm8gdHJhZGluZyBkYXkgYWRqdXN0bWVudAoqIE5vIGNhbGVuZGFyIGFkanVzdG1lbnRzCiogT25seSBhZGRpdGl2ZQoKRm9yIG11bHRpcGxpY2F0aXZlOiBUYWtlIGxvZ3MgdG8gZ2V0IG11bHRpcGxpY2F0aXZlIGRlY29tcG9zaXRpb24KVXNlIEJveC1Db3ggdHJhbnNmb3JtYXRpb25zIHRvIGdldCBvdGhlciBkZWNvbXBvc2l0aW9ucwoKIyMjIEV4YW1wbGUKCmBgYHtyfQp1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwIHw+CiAgbW9kZWwoU1RMKEVtcGxveWVkKSkgfD4KICBjb21wb25lbnRzKCkgfD4KICBhdXRvcGxvdCgpCmBgYAoKIyMjIFdlIGNhbiBjb250cm9sIHRoZSBjaGFuZ2UgaW4gc2Vhc29uYWwgcGF0dGVybiBhbmQgdHJlbmQgd2l0aCB3aW5kb3dzCgpgYGB7cn0KdXNfcmV0YWlsX2VtcGxveW1lbnRfMTk5MCB8PgogIG1vZGVsKFNUTChFbXBsb3llZCB+IHNlYXNvbih3aW5kb3c9MTUpICsgdHJlbmQod2luZG93PTY2NjUpKSkgfD4KICBjb21wb25lbnRzKCkgfD4KICBhdXRvcGxvdCgpCmBgYAoKT2JzZXJ2YXRpb25zCgoqIExvdCBvZiBzdHJ1Y3R1cmUgaW4gdGhlIHJlbWFpbmRlcgoqIEJlY2F1c2UgdHJlbmQgd2luZG93ID0gNjY2NQoqIFRyZW5kIGJlY29tZXMgYSBzdHJhaWdodCBsaW5lCiogV2hhdCBzaG91bGQgaGF2ZSBnb25lIGluIHRyZW5kIGdvZXMgdG8gcmVtYWluZGVyCiogYERlZmF1bHQgcGFyYW1zIHdvcmsgcHJldHR5IGdvb2RgCgojIyMgVHJlbmQgd2l0aCBzbWFsbCB3aW5kb3cKCmBgYHtyfQp1c19yZXRhaWxfZW1wbG95bWVudF8xOTkwIHw+CiAgbW9kZWwoU1RMKEVtcGxveWVkIH4gc2Vhc29uKHdpbmRvdz0xNSkgKyB0cmVuZCh3aW5kb3c9MTUpKSkgfD4KICBjb21wb25lbnRzKCkgfD4KICBhdXRvcGxvdCgpCmBgYAoKKiBPYnNlcnZhdGlvbnMKCiogV2l0aCB0cmVuZCB3aW5kb3cgPSAxNQoqIFdlIGNhcHR1cmUgdGhlIGludGVyZXN0aW5nIGJlaGF2aW91ciBvdXIgaW4gVHJlbmQKKiBOb2lzZSBnb2VzIHRvIHJlbWFpbmRlcgoqIE5vIHN0cm9uZyBzdHJ1Y3R1cmUgaW4gcmVtYWluZGVyCgojIyMgUm9idXN0IGFyZ3VtZW50IChkZWZhdWx0IGlzIGZhbHNlKQoKYGBge3J9CnVzX3JldGFpbF9lbXBsb3ltZW50XzE5OTAgfD4KICBtb2RlbChTVEwoRW1wbG95ZWQgfiBzZWFzb24od2luZG93PTE1KSArIHRyZW5kKHdpbmRvdz0xNSksIHJvYnVzdCA9IFRSVUUpKSB8PgogIGNvbXBvbmVudHMoKSB8PgogIGF1dG9wbG90KCkKYGBgCgojIyMgU3VtbWFyeQoKKiB0cmVuZCh3aW5kb3cgPSA/KSBjb250cm9scyB3aWdnbGluZXNzIG9mIHRyZW5kIGNvbXBvbmVudAogICogQmVjYXVzZSBvZiBMb2VzCiAgKiB3ZWlnaHRlZCBsZWFzdCBzcXVhcmVkIGZpdHMKICAqIHNtYWxsZXIgd2luZG93cywgZmV3ZXIgb2JzIGZvciBlYWNoIGxvY2FsIGxpbmVhciByZWdyZXNzaW9uCiAgCiogc2Vhc29uKHdpbmRvdyA9ID8pIGNvbnRyb2xzIHZhcmlhdGlvbnMgb24gc2Vhc29uYWwgY29tcG9uZW50CiAgKiBTYW1lIGNvbXBvbmVudCBpbiBkaWZmZXJlbnQgeWVhcnMKICAqIEhvdyBtYW55IHllYXJzIHRvIHVzZSB3aGVuIGVzdGltYXRpbmcgZWFjaCBvZiBzZWFzb25hbCBjb21wb25lbnQKKiBzZWFzb24od2luZG93ID0gJ3BlcmlvZGljJykgaXMgZXF1aXZhbGVudCB0byBpbmZpbml0ZSB3aW5kb3cKCkJ5IGRlZmF1bHQKCiogY2hvb3NlcyBzZWFzb24od2luZG93PTEzKQoqIENhbiBpbmNsdWRlIHRyYW5zZm9ybWF0aW9ucwoKYFNUTCBERUNPTVBPU0lUSU9OYAoKKiBEZWZhdWx0IHRyZW5kOiB3aW5kb3cgPSBuZXh0b2RkKGNlaWxpbmcoKDEuNSpwZXJpb2QpLygxLSgxLjUvcy53aW5kb3cpKSkpCiogRGVmYXVsdCBzZWFzb246IHdpbmRvdyA9IDEzCgpIb3cgZG9lcyBpdCB3b3JrPwoKKiBVcGRhdGVkIHRoZSB0cmVuZCBhbmQgc2Vhc29uYWwgY29tcG9uZW50IGl0ZXJhdGl2ZWx5CiogU3RhcnRzIG9mZiBieSBzYXlpbmcsIGxldHMgc3VwcG9zZSB0aGVyZSBpcyBubyB0cmVuZCwgZXN0aW1hdGUgc2Vhc29uYWxpdHkgYnkgY2xhc3NpY2FsIGRlY29tcAoqIFJlbW92ZXMgdGhhdCwgdHJpZXMgdG8gZXN0aW1hdGUgdHJlbmQgdXNpbmcgTE9FUyBjdXJ2ZQoqIEl0ZXJhdGVzCgpFeHBsYW5hdGlvbjoKCiogU3RhcnRzIHdpdGggVHQgPSAwCiogVXNlcyBhIG1peHR1cmUgb2YgYGxvZXNzYCBhbmQgYE1BYCB0byBzdWNlc3NpdmVseSByZWZpbmUgdGhlIGB0cmVuZGAgYW5kIGBzZWFzb25hbGAgZXN0aW1hdGVzCiogVGhlIHRyZW5kIHdpbmRvdyBjb250cm9scyBgbG9lc3NgIGJhbmR3aWR0aCBhcHBsaWVkIHRvIGRlYXNvbmFsaXNlZCB2YWx1ZXMKKiBUaGUgc2Vhc29uIHdpbmRvdyBjb250cm9scyBgbG9lc3NgIGJhbmR3aWR0aCBhcHBsaWVkIHRvIGRldHJlbmRlZCBzdWJzZXJpZXMKKiBSb2J1c3RiZXNzIHdlaWdodHMgYmFzZWQgb24gcmVtYWluZGVyCgojIyMgTElOSyBUTyBQQVBFUjoKCmh0dHA6Ly9iaXQubHkvc3RsMTk5MAoKCiMgVEhFIEVORAo=