Introduction and
Background
Here we have a dataset composed of historical records of the New York
Stock Exchange’s most famous index, the S&P 500. Made up of the top
500 companies for which trades across the United States are made, the
S&P 500 (or S&P), was believed to have had a market cap of just
over $57 trillion as of this past August. Since it’s
inception, the S&P has been used as a political benchmark for the
growth and overall health of the United States’ economy.
For my analysis, I wanted to specifically focus on what the opening
price of the S&P was at the first trading day of each year, which
ranged from January 2nd to January 4th depending on if the New Years’
holiday coincided with the weekend. On top of observing any possible
historical trends, I also used this data as grounds to test multiple
different time series modeling methods. A breakdown of the dataset’s
original structure is below.
|
Name
|
Meaning
|
Data_Type
|
|
Date
|
Date for that observation; YYYY-MM-DD form
|
Date
|
|
Open
|
S&P’s opening market price
|
double
|
|
High
|
That day’s highest recorded price
|
double
|
|
Low
|
That day’s lowest recorded price
|
double
|
|
Close
|
S&P’s closing market price
|
double
|
|
Adj Close
|
S&P’s closing market price, accounting for corporate activity*
|
double
|
|
Volume
|
Total number of shares traded in the market that day
|
double
|
- Corporate activity primarily includes, but is not limited to,
dividend payouts and stock splits
Model Creation
For my time series modeling experiments, I chose to divide my dataset
into separate portions, with the larger chunk being used for model
training and the later part being used for model testing. More
specifically, the S&P’s year-beginning market open from 1928 to 2010
I dedicated to training. While the observations from
2011 to 2020 will be used to test my model’s
accuracy.
I created four different time series models - Moving Average, Naive,
Seasonal Naive and Drift - using the training data. Each model was set
to have a forecast horizon (number of future intervals to predict) of
10. A table summary of each model’s predictions for the ten years after
our training data’s end is below.
##### Moving Average Model
Moving_Average = round(meanf(TS_Training_Data, h=10)$mean, 4)
# argument h represents the forecast horizon, which is the number of intervals into the future we are predicting values for
# Setting h=10 since we used 10 for the sample size of the testing data
##### Naive Model
Naive = round(naive(TS_Training_Data, h=10)$mean, 4)
##### Seasonal Naive Model
Seasonal_Naive = round(snaive(TS_Training_Data, h=10)$mean, 4)
##### Drift Model
Drift = round(rwf(TS_Training_Data, h=10, drift=TRUE)$mean, 4)
# function rwf stands for "random walk with drift"
# Note that we MUST set drift = TRUE in order for it to be a proper drift model
Year = c("2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020")
Baseline_Models = cbind(Year, Moving_Average, Naive, Seasonal_Naive, Drift)
colnames(Baseline_Models) = c("Year", "Moving Average", "Naive", "Seasonal Naive", "Drift")
kable(Baseline_Models, caption = "Model Forecasting Projections")
Model Forecasting Projections
| 2011 |
283.2408 |
1116.5601 |
1116.5601 |
1129.9601 |
| 2012 |
283.2408 |
1116.5601 |
1116.5601 |
1143.3601 |
| 2013 |
283.2408 |
1116.5601 |
1116.5601 |
1156.7601 |
| 2014 |
283.2408 |
1116.5601 |
1116.5601 |
1170.1601 |
| 2015 |
283.2408 |
1116.5601 |
1116.5601 |
1183.5601 |
| 2016 |
283.2408 |
1116.5601 |
1116.5601 |
1196.9601 |
| 2017 |
283.2408 |
1116.5601 |
1116.5601 |
1210.3601 |
| 2018 |
283.2408 |
1116.5601 |
1116.5601 |
1223.7601 |
| 2019 |
283.2408 |
1116.5601 |
1116.5601 |
1237.1601 |
| 2020 |
283.2408 |
1116.5601 |
1116.5601 |
1250.5601 |
Looking at our summary table, we see a few interesting takeaways.
First, we see that the year-to-year predicted values from the
moving average model are far beneath those of the other
3 models. This is because the moving average method operates on the
assumption that all future values will be equal to the mean of all
previous values. A large amount of our training data (58 of the 73)
years, had a market open below $200.
We also notice there is total equality between the naive and
seasonal naive models. The naive method assumes that all future
observations will have equal value to that of the most previous
observation (which explains why the predicted value is the same for each
year in the string of forecasts). Meanwhile, the seasonal naive approach
applies the premise of the naive method to data that operates in a
cyclical manner (think frequencies of weather related events tied to
seasonal changes). In the case of our dataset, each S&P market
opening is occurring at approximately the same day every year,
making the naive and seasonal naive model results practically
identical.
Lastly, we see that the drift model is the only one in which
forecasted values differ from year to year. Unlike the other three
methods, the drift method does account for historical averages in rate
of increase and decrease as well as averages in overall value as well.
Given that the S&P has always gained value when looked at from a
long term perspective, it is not surprising that our drift forecasts
show an average increase of about 13 to 14 USD in projected market
opening from one year to the next.
Model Forecast
Visualizations
After examining the model forecasts year-by-year, I decided to graph
each model’s predictions alongside the known historical data to allow
for a quick examination of how accurate each model was. The first graph
consists of the entire scope of this dataset’s observations (1929 to
2020), while the second graph I constrained to this century’s market
opens. It should be noted that, in both graphs, the representation of
the naive model forecasts can be difficult to see as they perfectly
overlap with those of the seasonal naive model.
DF_Predictions = cbind(2011:2020, data.frame(Moving_Average), data.frame(Naive), data.frame(Seasonal_Naive), data.frame(Drift))
colnames(DF_Predictions) = c("Year", "Moving Average", "Naive", "Seasonal Naive", "Drift")
Full_Graph = (ggplot() +
geom_line(data = First_Day, mapping = aes (x = Year, y = Open)) +
geom_point(data = First_Day, mapping = aes(x = Year, y = Open)) +
labs(x = "Year", y = "S&P 500 Open ($)", title ="S&P 500 First Market Open of the Year") +
geom_line(data = DF_Predictions, mapping = aes (x = Year, y = Moving_Average)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Moving_Average, color = "Moving Average")) +
geom_line(data = DF_Predictions, mapping = aes (x = Year, y = Naive)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Naive, color = "Naive")) +
geom_line(data = DF_Predictions, mapping=aes(x = Year, y = `Seasonal Naive`)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Seasonal_Naive, color = "Seasonal Naive")) +
geom_line(data = DF_Predictions, mapping=aes(x = Year, y = `Drift`)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Drift, color = "Drift")) +
scale_x_continuous(
limits = c(1925, 2020),
breaks = seq(1930, 2020, 10)
) +
scale_y_continuous(
limits = c(0, 3250),
breaks = seq(0, 3500, 500)
))
Full_Graph

########################
Reduced_Graph = (ggplot() +
geom_line(data = First_Day, mapping = aes (x = Year, y = Open)) +
geom_point(data = First_Day, mapping = aes(x = Year, y = Open)) +
labs(x = "Year", y = "S&P 500 Open ($)", title ="S&P 500 First Market Open of the Year (2000 - 2020)") +
geom_line(data = DF_Predictions, mapping = aes (x = Year, y = Moving_Average)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Moving_Average, color = "Moving Average",)) +
geom_line(data = DF_Predictions, mapping = aes (x = Year, y = Naive)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Naive, color = "Naive")) +
geom_line(data = DF_Predictions, mapping=aes(x = Year, y = `Seasonal Naive`)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Seasonal_Naive, color = "Seasonal Naive")) +
geom_line(data = DF_Predictions, mapping=aes(x = Year, y = `Drift`)) +
geom_point(data = DF_Predictions, mapping = aes (x = Year, y = Drift, color = "Drift")) +
scale_x_continuous(
limits = c(2000, 2020),
breaks = seq(2000, 2020, 4)
) +
scale_y_continuous(
limits = c(250, 3250),
breaks = seq(250, 3250, 500)
)
)
Reduced_Graph
We can see from our two graphs that, like seen in the summary table
above, the moving average model’s forecasts are well beneath those of
the other three models. That is because of the much greater influence
that the first roughly 50 years of the S&P’s opening market price
has on the moving average’s calculation than it does on the naive,
seasonal naive or drift. The average opening price from 1928 to 1978 was
only about $45.64.
Additionally, while our other three models’ forecasts resemble that
of the index’ true opening value far greater than those of our moving
average model, they also consistently underestimated. The drift model’s
predicted values were the most accurate, which is not suprising given
that, as previously said, the drift approach does account for historical
trends in increase or decrease. The reason the drift forecasts’ trend
was in the proper direction (increasing over time), but at a rate far
lesser than the true rate of market open increase is due to the
extraordinary weakness of the S&P opening’s upward trend throughout
the first five decades or so of our dataset.
Model Accuracy
Measures
Finally, I wanted to quantify the strength of my four models using
some standard measures of modeling accuracy. Below is a breakdown of
each model’s mean error (ME), mean square error (MSE) and mean absolute
prediction error (MAPE). Mean error and mean square error are considered
measures of absolute error, and are a good reference statistic to
summarize how “off” our models’ forecasts were on average. Meanwhile,
MAPE is called a measure of relative error, as it tells us on average
how “off” our models’ forecasts were as a percent.
None of the metrics calculated in the table below are particularly
surprising. They are in-line with our previous graphical displays and
forecast summary table. Our drift model is the most accurate, our naive
and seasonal naive models are slightly worse, and the moving average is
significantly less useful than the others.
# Will find mean error (ME), mean squared error (MSE) and mean absolute prediction error (MAPE) for each of the 4 baseline models. Then cbind them all into a table and output with kable function.
### ME first
Moving_Average_Errors = data.frame(DF_Predictions$`Moving Average` - Testing_Data$Open)
Moving_Average_ME = sum(Moving_Average_Errors)/nrow(Moving_Average_Errors)
Naive_Errors = data.frame(DF_Predictions$Naive - Testing_Data$Open)
Naive_ME = sum(Naive_Errors)/nrow(Naive_Errors)
Seasonal_Naive_Errors = data.frame(DF_Predictions$`Seasonal Naive` - Testing_Data$Open)
Seasonal_Naive_ME = sum(Seasonal_Naive_Errors)/nrow(Seasonal_Naive_Errors)
Drift_Errors = data.frame(DF_Predictions$Drift - Testing_Data$Open)
Drift_ME = sum(Drift_Errors)/nrow(Drift_Errors)
All_Model_MEs = rbind(Moving_Average_ME, Naive_ME, Seasonal_Naive_ME, Drift_ME)
### MSE second
# Create squared error data frames
Moving_Average_SQErrors = data.frame(Moving_Average_Errors^2)
Naive_SQErrors = data.frame(Naive_Errors^2)
Seasonal_Naive_SQErrors = data.frame(Seasonal_Naive_Errors^2)
Drift_SQErrors = data.frame(Drift_Errors^2)
# Use squared error data frames to create MSE objects
Moving_Average_MSE = sum(Moving_Average_SQErrors)/nrow(Moving_Average_SQErrors)
Naive_MSE = sum(Naive_SQErrors)/nrow(Naive_SQErrors)
Seasonal_Naive_MSE = sum(Seasonal_Naive_SQErrors)/nrow(Seasonal_Naive_SQErrors)
Drift_MSE = sum(Drift_SQErrors)/nrow(Drift_SQErrors)
All_Model_MSEs = rbind(Moving_Average_MSE, Naive_MSE, Seasonal_Naive_MSE, Drift_MSE)
#MAPE third
Moving_Average_PEI = data.frame((Moving_Average_Errors/Testing_Data$Open)*100)
Moving_Average_MAPE = round(sum(abs(Moving_Average_PEI))/nrow(Moving_Average_PEI),4)
Naive_PEI = data.frame((Naive_Errors/Testing_Data$Open)*100)
Naive_MAPE = round(sum(abs(Naive_PEI))/nrow(Naive_PEI),4)
Seasonal_Naive_PEI = data.frame((Seasonal_Naive_Errors/Testing_Data$Open)*100)
Seasonal_Naive_MAPE = round(sum(abs(Seasonal_Naive_PEI))/nrow(Seasonal_Naive_PEI),4)
Drift_PEI = data.frame((Drift_Errors/Testing_Data$Open)*100)
Drift_MAPE = round(sum(abs(Drift_PEI))/nrow(Drift_PEI),4)
All_Model_MAPEs = rbind(Moving_Average_MAPE, Naive_MAPE, Seasonal_Naive_MAPE, Drift_MAPE)
Model_Accuracy = cbind(All_Model_MEs, All_Model_MSEs, All_Model_MAPEs)
rownames(Model_Accuracy) = c("Moving Average", "Naive", "Seasonal Naive", "Drift")
colnames(Model_Accuracy) = c("ME", "MSE", "MAPE")
kable(Model_Accuracy, caption = "Model Accuracy Summary")
Model Accuracy Summary
| Moving Average |
-1771.0152 |
3510134 |
84.8822 |
| Naive |
-937.6959 |
1252913 |
40.4043 |
| Seasonal Naive |
-937.6959 |
1252913 |
40.4043 |
| Drift |
-863.9959 |
1076191 |
37.0768 |
Conclusion
In conclusion, we can see that the straightforward application of
these four standard and common time series modeling methods on the
historical stock market data that was examined, were not particularly
useful in forecasting future S&P 500 opening market prices. This was
largely due to the consistently low market opens throughout the first 50
or so years of the S&P index. In the future, it would be interesting
to examine the model accuracy of these methods - particularly the moving
average and drift - if the range of the training data was greatly
reduced to only include more recent decades.
References:
Original Dataset Source:
Dataset Download Link via Github:
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgWWVhci10by1ZZWFyIFMmUCA1MDAgT3BlbmluZyBQcmljZSIKYXV0aG9yOiAiQ2hyaXMgQmFobSIKZGF0ZTogIjIwMjUtMTEtMTQiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ193aWR0aDogNgogICAgZmlnX2hlaWdodDogNAogICAgZmlnX2NhcHRpb246IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogbHVtZW4KICAgIGhpZ2hsaWdodDogdGFuZ28KICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNAotLS0KCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KZGl2I1RPQyBsaSB7ICAgICAvKiB0YWJsZSBvZiBjb250ZW50ICAqLwogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsKICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsKICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7CiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7Cn0KCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovCiAgZm9udC1zaXplOiAyNHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgY29sb3I6IERhcmtSZWQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgY29sb3I6IERhcmtCbHVlOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDEgeyAvKiBIZWFkZXIgMSAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBkYXJrcmVkOwogICAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoMiB7IC8qIEhlYWRlciAyIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgpoMyB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDE2cHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgpoNCB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDE0cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBkYXJrcmVkOwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKLyogQWRkIGRvdHMgYWZ0ZXIgbnVtYmVyZWQgaGVhZGVycyAqLwouaGVhZGVyLXNlY3Rpb24tbnVtYmVyOjphZnRlciB7CiAgY29udGVudDogIi4iOwp9CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsgICAgICAgICAgICAgICAgICAgICAgIyB1c2UgY29uZGl0aW9uYWwgc3RhdGVtZW50IHRvIGRldGVjdAogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpICAgICAgICAgICAgICAgICAgIyB3aGV0aGVyIGEgcGFja2FnZSB3YXMgaW5zdGFsbGVkIGluCiAgIGxpYnJhcnkoa25pdHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdXIgbWFjaGluZS4gSWYgbm90LCBpbnN0YWxsIGl0IGFuZAp9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBsb2FkIGl0IHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4KCmlmICghcmVxdWlyZSh0aWR5dmVyc2UpKSB7bGlicmFyeSh0aWR5dnNlcnNlKX0gCgppZiAoIXJlcXVpcmUoR0dhbGx5KSkge2xpYnJhcnkoR0dhbGx5KX0gCgppZiAoIXJlcXVpcmUoa2FibGVFeHRyYSkpIHtsaWJyYXJ5KGthYmxlRXh0cmEpfSAKCmlmICghcmVxdWlyZShnZ3Bsb3QyKSkge2xpYnJhcnkoZ2dwbG90Mil9IAoKaWYgKCFyZXF1aXJlKGNhcikpIHtsaWJyYXJ5KGNhcil9IAoKaWYgKCFyZXF1aXJlKGRwbHlyKSkge2xpYnJhcnkoZHBseXIpfSAKCmlmICghcmVxdWlyZShwYW5kZXIpKSB7bGlicmFyeShwYW5kZXIpfSAKCmlmICghcmVxdWlyZShmb3JlY2FzdCkpIHtsaWJyYXJ5KGZvcmVjYXN0KX0gCgppZiAoIXJlcXVpcmUobHVicmlkYXRlKSkge2xpYnJhcnkobHVicmlkYXRlKX0gCgppZiAoIXJlcXVpcmUoInNjYWxlcyIpKSB7Cmluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIApsaWJyYXJ5KCJzY2FsZXMiKSAKfQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KAoJZWNobyA9IFRSVUUsCgltZXNzYWdlID0gRkFMU0UsCgl3YXJuaW5nID0gRkFMU0UsCgljb21tZW50ID0gTkEsCglyZXN1bHRzID0gVFJVRQopCmBgYAoKIyBJbnRyb2R1Y3Rpb24gYW5kIEJhY2tncm91bmQKSGVyZSB3ZSBoYXZlIGEgZGF0YXNldCBjb21wb3NlZCBvZiBoaXN0b3JpY2FsIHJlY29yZHMgb2YgdGhlIE5ldyBZb3JrIFN0b2NrIEV4Y2hhbmdlJ3MgbW9zdCBmYW1vdXMgaW5kZXgsIHRoZSBTJlAgNTAwLiBNYWRlIHVwIG9mIHRoZSB0b3AgNTAwIGNvbXBhbmllcyBmb3Igd2hpY2ggdHJhZGVzIGFjcm9zcyB0aGUgVW5pdGVkIFN0YXRlcyBhcmUgbWFkZSwgdGhlIFMmUCA1MDAgKG9yIFMmUCksIHdhcyBiZWxpZXZlZCB0byBoYXZlIGhhZCBhIG1hcmtldCBjYXAgb2YganVzdCBvdmVyICoqJDU3IHRyaWxsaW9uKiogYXMgb2YgdGhpcyBwYXN0IEF1Z3VzdC4gU2luY2UgaXQncyBpbmNlcHRpb24sIHRoZSBTJlAgaGFzIGJlZW4gdXNlZCBhcyBhIHBvbGl0aWNhbCBiZW5jaG1hcmsgZm9yIHRoZSBncm93dGggYW5kIG92ZXJhbGwgaGVhbHRoIG9mIHRoZSBVbml0ZWQgU3RhdGVzJyBlY29ub215LiAKCkZvciBteSBhbmFseXNpcywgSSB3YW50ZWQgdG8gc3BlY2lmaWNhbGx5IGZvY3VzIG9uIHdoYXQgdGhlIG9wZW5pbmcgcHJpY2Ugb2YgdGhlIFMmUCB3YXMgYXQgdGhlIGZpcnN0IHRyYWRpbmcgZGF5IG9mIGVhY2ggeWVhciwgd2hpY2ggcmFuZ2VkIGZyb20gSmFudWFyeSAybmQgdG8gSmFudWFyeSA0dGggZGVwZW5kaW5nIG9uIGlmIHRoZSBOZXcgWWVhcnMnIGhvbGlkYXkgY29pbmNpZGVkIHdpdGggdGhlIHdlZWtlbmQuIE9uIHRvcCBvZiBvYnNlcnZpbmcgYW55IHBvc3NpYmxlIGhpc3RvcmljYWwgdHJlbmRzLCBJIGFsc28gdXNlZCB0aGlzIGRhdGEgYXMgZ3JvdW5kcyB0byB0ZXN0IG11bHRpcGxlIGRpZmZlcmVudCB0aW1lIHNlcmllcyBtb2RlbGluZyBtZXRob2RzLiBBIGJyZWFrZG93biBvZiB0aGUgZGF0YXNldCdzIG9yaWdpbmFsIHN0cnVjdHVyZSBpcyBiZWxvdy4KCmBgYHtyIERhdGEgTG9hZGluZyBhbmQgQ2xlYW5pbmcsIGluY2x1ZGU9RkFMU0V9CkRhdGFfVXJsPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0NocmlzQjIzMjMvU1RBMzIxL3JlZnMvaGVhZHMvbWFpbi9TUFguY3N2IgoKRGF0YSA9IHJlYWRfZGVsaW0oRGF0YV9VcmwsIGRlbGltID0gIiwiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKc3VtKGlzLm5hKERhdGEpKQojIE5vIG1pc3NpbmcgdmFsdWVzIGluIGRhdGFzZXQKCmdsaW1wc2UoRGF0YSkKIyBBbGwgdmFyaWFibGVzIGZvcm1hdHRlZCBwcm9wZXJseQogICMgT3BlbiwgSGlnaCwgTG93LCBDbG9zZSBhbmQgQWRqIENsb3NlIGFsbCBpbiBzaW5nbGUgZG9sbGFyIHVuaXRzCgoKIyBCZWxvdyBjcmVhdGVkIGEgc3Vic2V0IG9mIHRoZSBkYXRhLCBsb29raW5nIGF0IGZpcnN0IHRyYWRpbmcgZGF5IG9mIGVhY2ggeWVhcgpGaXJzdF9EYXkgPSBEYXRhICU+JQogICMgRXh0cmFjdCB5ZWFyIGZyb20gZWFjaCBkYXRlCiAgbXV0YXRlKFllYXIgPSB5ZWFyKERhdGUpKSAlPiUKICAjIEZpbHRlciBvbmx5IHllYXJzIDE5MjjigJMyMDE5CiAgZmlsdGVyKFllYXIgPj0gMTkyOCkgJT4lCiAgIyBHcm91cCBieSB5ZWFyCiAgZ3JvdXBfYnkoWWVhcikgJT4lCiAgIyBQaWNrIHRoZSByb3cgd2l0aCB0aGUgZWFybGllc3QgZGF0ZSBpbiB0aGF0IHllYXIKICBzbGljZV9taW4oRGF0ZSwgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKSAlPiUKICB1bmdyb3VwKCkKCkZpcnN0X0RheV9PcGVuaW5nID0gRmlyc3RfRGF5WyAsYygiRGF0ZSIsICJPcGVuIildCgojIFllYXJzIDE5MjggdG8gMjAxMCB3aWxsIGJlIGZvciBtb2RlbCB0cmFpbmluZwpUcmFpbmluZ19EYXRhID0gRmlyc3RfRGF5X09wZW5pbmdbMTo4MyxdCgojIFllYXJzIDIwMTEgdG8gMjAyMCB3aWxsIGJlIGZvciBtb2RlbCB0ZXN0aW5nClRlc3RpbmdfRGF0YSA9IEZpcnN0X0RheV9PcGVuaW5nWzg0OjkzLF0KCiMgV2UgY3JlYXRlIHRoZSB0aW1lIHNlcmllcyBvYmplY3Qgd2l0aCB0aGUgdHMoKSBmdW5jdGlvbiwgY29taW5nIGZyb20gZm9yZWNhc3QgcGFja2FnZQpUU19UcmFpbmluZ19EYXRhID0gdHMoVHJhaW5pbmdfRGF0YSRPcGVuLAogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSAxOTI4LAogICAgICAgICAgICAgICAgICAgICAgZW5kID0gMjAxMCwKICAgICAgICAgICAgICAgICAgICAgIGZyZXF1ZW5jeSA9IDEpCgppbnZpc2libGUoVFNfVHJhaW5pbmdfRGF0YSkgCmBgYAoKYGBge3IgVmFyaWFibGUgVGFibGUsIGVjaG89RkFMU0V9CmxpYnJhcnkoa25pdHIpCiNnbGltcHNlKERhdGEpCgpWYXJfVGFibGUgPSBkYXRhLmZyYW1lKAogIE5hbWUgPSBjKCJEYXRlIiwKICAgICAgICAgICAiT3BlbiIsIAogICAgICAgICAgICJIaWdoIiwgCiAgICAgICAgICAgIkxvdyIsCiAgICAgICAgICAgIkNsb3NlIiwgCiAgICAgICAgICAgIkFkaiBDbG9zZSIsIAogICAgICAgICAgICJWb2x1bWUiKSwKICAKICBNZWFuaW5nID0gYygiRGF0ZSBmb3IgdGhhdCBvYnNlcnZhdGlvbjsgWVlZWS1NTS1ERCBmb3JtIiwgCiAgICAgICAgICAgICAgIlMmUCdzIG9wZW5pbmcgbWFya2V0IHByaWNlIiwKICAgICAgICAgICAgICAiVGhhdCBkYXkncyBoaWdoZXN0IHJlY29yZGVkIHByaWNlIiwgCiAgICAgICAgICAgICAgIlRoYXQgZGF5J3MgbG93ZXN0IHJlY29yZGVkIHByaWNlIiwgCiAgICAgICAgICAgICAgIlMmUCdzIGNsb3NpbmcgbWFya2V0IHByaWNlIiwgIAogICAgICAgICAgICAgICJTJlAncyBjbG9zaW5nIG1hcmtldCBwcmljZSwgYWNjb3VudGluZyBmb3IgY29ycG9yYXRlIGFjdGl2aXR5KiIsCiAgICAgICAgICAgICAgIlRvdGFsIG51bWJlciBvZiBzaGFyZXMgdHJhZGVkIGluIHRoZSBtYXJrZXQgdGhhdCBkYXkiKSwgCiAgICAgICAgICAgICAgCiAgCiAgRGF0YV9UeXBlID0gYygiRGF0ZSIsICJkb3VibGUiLCAiZG91YmxlIiwgImRvdWJsZSIsICJkb3VibGUiLCAiZG91YmxlIiwgImRvdWJsZSIpKQoKa2FibGUoVmFyX1RhYmxlKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiKQpgYGAKKiBDb3Jwb3JhdGUgYWN0aXZpdHkgcHJpbWFyaWx5IGluY2x1ZGVzLCBidXQgaXMgbm90IGxpbWl0ZWQgdG8sIGRpdmlkZW5kIHBheW91dHMgYW5kIHN0b2NrIHNwbGl0cwoKIyBNb2RlbCBDcmVhdGlvbgpGb3IgbXkgdGltZSBzZXJpZXMgbW9kZWxpbmcgZXhwZXJpbWVudHMsIEkgY2hvc2UgdG8gZGl2aWRlIG15IGRhdGFzZXQgaW50byBzZXBhcmF0ZSBwb3J0aW9ucywgd2l0aCB0aGUgbGFyZ2VyIGNodW5rIGJlaW5nIHVzZWQgZm9yIG1vZGVsIHRyYWluaW5nIGFuZCB0aGUgbGF0ZXIgcGFydCBiZWluZyB1c2VkIGZvciBtb2RlbCB0ZXN0aW5nLiBNb3JlIHNwZWNpZmljYWxseSwgdGhlIFMmUCdzIHllYXItYmVnaW5uaW5nIG1hcmtldCBvcGVuIGZyb20gMTkyOCB0byAyMDEwIEkgZGVkaWNhdGVkIHRvICoqdHJhaW5pbmcqKi4gV2hpbGUgdGhlIG9ic2VydmF0aW9ucyBmcm9tIDIwMTEgdG8gMjAyMCB3aWxsIGJlIHVzZWQgdG8gKip0ZXN0KiogbXkgbW9kZWwncyBhY2N1cmFjeS4KCkkgY3JlYXRlZCBmb3VyIGRpZmZlcmVudCB0aW1lIHNlcmllcyBtb2RlbHMgLSBNb3ZpbmcgQXZlcmFnZSwgTmFpdmUsIFNlYXNvbmFsIE5haXZlIGFuZCBEcmlmdCAtIHVzaW5nIHRoZSB0cmFpbmluZyBkYXRhLiBFYWNoIG1vZGVsIHdhcyBzZXQgdG8gaGF2ZSBhIGZvcmVjYXN0IGhvcml6b24gKG51bWJlciBvZiBmdXR1cmUgaW50ZXJ2YWxzIHRvIHByZWRpY3QpIG9mIDEwLiBBIHRhYmxlIHN1bW1hcnkgb2YgZWFjaCBtb2RlbCdzIHByZWRpY3Rpb25zIGZvciB0aGUgdGVuIHllYXJzIGFmdGVyIG91ciB0cmFpbmluZyBkYXRhJ3MgZW5kIGlzIGJlbG93LgpgYGB7ciwgQmFzZWxpbmUgRm9yZWNhc3RpbmcgTW9kZWwgQ3JlYXRpb259CiMjIyMjIE1vdmluZyBBdmVyYWdlIE1vZGVsCk1vdmluZ19BdmVyYWdlID0gcm91bmQobWVhbmYoVFNfVHJhaW5pbmdfRGF0YSwgaD0xMCkkbWVhbiwgNCkKICAjIGFyZ3VtZW50IGggcmVwcmVzZW50cyB0aGUgZm9yZWNhc3QgaG9yaXpvbiwgd2hpY2ggaXMgdGhlIG51bWJlciBvZiBpbnRlcnZhbHMgaW50byB0aGUgZnV0dXJlIHdlIGFyZSBwcmVkaWN0aW5nIHZhbHVlcyBmb3IKICAjIFNldHRpbmcgaD0xMCBzaW5jZSB3ZSB1c2VkIDEwIGZvciB0aGUgc2FtcGxlIHNpemUgb2YgdGhlIHRlc3RpbmcgZGF0YQoKIyMjIyMgTmFpdmUgTW9kZWwKTmFpdmUgPSByb3VuZChuYWl2ZShUU19UcmFpbmluZ19EYXRhLCBoPTEwKSRtZWFuLCA0KQoKIyMjIyMgU2Vhc29uYWwgTmFpdmUgTW9kZWwKU2Vhc29uYWxfTmFpdmUgPSByb3VuZChzbmFpdmUoVFNfVHJhaW5pbmdfRGF0YSwgaD0xMCkkbWVhbiwgNCkKCiMjIyMjIERyaWZ0IE1vZGVsCkRyaWZ0ID0gcm91bmQocndmKFRTX1RyYWluaW5nX0RhdGEsIGg9MTAsIGRyaWZ0PVRSVUUpJG1lYW4sIDQpCiAgIyBmdW5jdGlvbiByd2Ygc3RhbmRzIGZvciAicmFuZG9tIHdhbGsgd2l0aCBkcmlmdCIKICAjIE5vdGUgdGhhdCB3ZSBNVVNUIHNldCBkcmlmdCA9IFRSVUUgaW4gb3JkZXIgZm9yIGl0IHRvIGJlIGEgcHJvcGVyIGRyaWZ0IG1vZGVsCgpZZWFyID0gYygiMjAxMSIsICIyMDEyIiwgIjIwMTMiLCAiMjAxNCIsICIyMDE1IiwgIjIwMTYiLCAiMjAxNyIsICIyMDE4IiwgIjIwMTkiLCAiMjAyMCIpCgpCYXNlbGluZV9Nb2RlbHMgPSBjYmluZChZZWFyLCBNb3ZpbmdfQXZlcmFnZSwgTmFpdmUsIFNlYXNvbmFsX05haXZlLCBEcmlmdCkKCmNvbG5hbWVzKEJhc2VsaW5lX01vZGVscykgPSBjKCJZZWFyIiwgIk1vdmluZyBBdmVyYWdlIiwgIk5haXZlIiwgIlNlYXNvbmFsIE5haXZlIiwgIkRyaWZ0IikKCmthYmxlKEJhc2VsaW5lX01vZGVscywgY2FwdGlvbiA9ICJNb2RlbCBGb3JlY2FzdGluZyBQcm9qZWN0aW9ucyIpCmBgYApMb29raW5nIGF0IG91ciBzdW1tYXJ5IHRhYmxlLCB3ZSBzZWUgYSBmZXcgaW50ZXJlc3RpbmcgdGFrZWF3YXlzLiAKCi0gRmlyc3QsIHdlIHNlZSB0aGF0IHRoZSB5ZWFyLXRvLXllYXIgcHJlZGljdGVkIHZhbHVlcyBmcm9tIHRoZSBtb3ZpbmcgYXZlcmFnZSBtb2RlbCBhcmUgKipmYXIqKiBiZW5lYXRoIHRob3NlIG9mIHRoZSBvdGhlciAzIG1vZGVscy4gVGhpcyBpcyBiZWNhdXNlIHRoZSBtb3ZpbmcgYXZlcmFnZSBtZXRob2Qgb3BlcmF0ZXMgb24gdGhlIGFzc3VtcHRpb24gdGhhdCBhbGwgZnV0dXJlIHZhbHVlcyB3aWxsIGJlIGVxdWFsIHRvIHRoZSBtZWFuIG9mIGFsbCBwcmV2aW91cyB2YWx1ZXMuIEEgbGFyZ2UgYW1vdW50IG9mIG91ciB0cmFpbmluZyBkYXRhICg1OCBvZiB0aGUgNzMpIHllYXJzLCBoYWQgYSBtYXJrZXQgb3BlbiBiZWxvdyAkMjAwLgoKLSBXZSBhbHNvIG5vdGljZSB0aGVyZSBpcyB0b3RhbCBlcXVhbGl0eSBiZXR3ZWVuIHRoZSBuYWl2ZSBhbmQgc2Vhc29uYWwgbmFpdmUgbW9kZWxzLiBUaGUgbmFpdmUgbWV0aG9kIGFzc3VtZXMgdGhhdCBhbGwgZnV0dXJlIG9ic2VydmF0aW9ucyB3aWxsIGhhdmUgZXF1YWwgdmFsdWUgdG8gdGhhdCBvZiB0aGUgbW9zdCBwcmV2aW91cyBvYnNlcnZhdGlvbiAod2hpY2ggZXhwbGFpbnMgd2h5IHRoZSBwcmVkaWN0ZWQgdmFsdWUgaXMgdGhlIHNhbWUgZm9yIGVhY2ggeWVhciBpbiB0aGUgc3RyaW5nIG9mIGZvcmVjYXN0cykuIE1lYW53aGlsZSwgdGhlIHNlYXNvbmFsIG5haXZlIGFwcHJvYWNoIGFwcGxpZXMgdGhlIHByZW1pc2Ugb2YgdGhlIG5haXZlIG1ldGhvZCB0byBkYXRhIHRoYXQgb3BlcmF0ZXMgaW4gYSBjeWNsaWNhbCBtYW5uZXIgKHRoaW5rIGZyZXF1ZW5jaWVzIG9mIHdlYXRoZXIgcmVsYXRlZCBldmVudHMgdGllZCB0byBzZWFzb25hbCBjaGFuZ2VzKS4gSW4gdGhlIGNhc2Ugb2Ygb3VyIGRhdGFzZXQsIGVhY2ggUyZQIG1hcmtldCBvcGVuaW5nIGlzIG9jY3VycmluZyBhdCAqYXBwcm94aW1hdGVseSogdGhlIHNhbWUgZGF5IGV2ZXJ5IHllYXIsIG1ha2luZyB0aGUgbmFpdmUgYW5kIHNlYXNvbmFsIG5haXZlIG1vZGVsIHJlc3VsdHMgcHJhY3RpY2FsbHkgaWRlbnRpY2FsLgoKLSBMYXN0bHksIHdlIHNlZSB0aGF0IHRoZSBkcmlmdCBtb2RlbCBpcyB0aGUgb25seSBvbmUgaW4gd2hpY2ggZm9yZWNhc3RlZCB2YWx1ZXMgZGlmZmVyIGZyb20geWVhciB0byB5ZWFyLiBVbmxpa2UgdGhlIG90aGVyIHRocmVlIG1ldGhvZHMsIHRoZSBkcmlmdCBtZXRob2QgZG9lcyBhY2NvdW50IGZvciBoaXN0b3JpY2FsIGF2ZXJhZ2VzIGluIHJhdGUgb2YgaW5jcmVhc2UgYW5kIGRlY3JlYXNlIGFzIHdlbGwgYXMgYXZlcmFnZXMgaW4gb3ZlcmFsbCB2YWx1ZSBhcyB3ZWxsLiBHaXZlbiB0aGF0IHRoZSBTJlAgaGFzIGFsd2F5cyBnYWluZWQgdmFsdWUgd2hlbiBsb29rZWQgYXQgZnJvbSBhIGxvbmcgdGVybSBwZXJzcGVjdGl2ZSwgaXQgaXMgbm90IHN1cnByaXNpbmcgdGhhdCBvdXIgZHJpZnQgZm9yZWNhc3RzIHNob3cgYW4gYXZlcmFnZSBpbmNyZWFzZSBvZiBhYm91dCAxMyB0byAxNCBVU0QgaW4gcHJvamVjdGVkIG1hcmtldCBvcGVuaW5nIGZyb20gb25lIHllYXIgdG8gdGhlIG5leHQuCgojIE1vZGVsIEZvcmVjYXN0IFZpc3VhbGl6YXRpb25zCkFmdGVyIGV4YW1pbmluZyB0aGUgbW9kZWwgZm9yZWNhc3RzIHllYXItYnkteWVhciwgSSBkZWNpZGVkIHRvIGdyYXBoIGVhY2ggbW9kZWwncyBwcmVkaWN0aW9ucyBhbG9uZ3NpZGUgdGhlIGtub3duIGhpc3RvcmljYWwgZGF0YSB0byBhbGxvdyBmb3IgYSBxdWljayBleGFtaW5hdGlvbiBvZiBob3cgYWNjdXJhdGUgZWFjaCBtb2RlbCB3YXMuIFRoZSBmaXJzdCBncmFwaCBjb25zaXN0cyBvZiB0aGUgZW50aXJlIHNjb3BlIG9mIHRoaXMgZGF0YXNldCdzIG9ic2VydmF0aW9ucyAoMTkyOSB0byAyMDIwKSwgd2hpbGUgdGhlIHNlY29uZCBncmFwaCBJIGNvbnN0cmFpbmVkIHRvIHRoaXMgY2VudHVyeSdzIG1hcmtldCBvcGVucy4gSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQsIGluIGJvdGggZ3JhcGhzLCB0aGUgcmVwcmVzZW50YXRpb24gb2YgdGhlIG5haXZlIG1vZGVsIGZvcmVjYXN0cyBjYW4gYmUgZGlmZmljdWx0IHRvIHNlZSBhcyB0aGV5IHBlcmZlY3RseSBvdmVybGFwIHdpdGggdGhvc2Ugb2YgdGhlIHNlYXNvbmFsIG5haXZlIG1vZGVsLgpgYGB7ciBHcmFwaHMsIGZpZy5hbGlnbj0nY2VudGVyJywgd2FybmluZz1GQUxTRX0KREZfUHJlZGljdGlvbnMgPSBjYmluZCgyMDExOjIwMjAsIGRhdGEuZnJhbWUoTW92aW5nX0F2ZXJhZ2UpLCBkYXRhLmZyYW1lKE5haXZlKSwgZGF0YS5mcmFtZShTZWFzb25hbF9OYWl2ZSksIGRhdGEuZnJhbWUoRHJpZnQpKQogIGNvbG5hbWVzKERGX1ByZWRpY3Rpb25zKSA9IGMoIlllYXIiLCAiTW92aW5nIEF2ZXJhZ2UiLCAiTmFpdmUiLCAiU2Vhc29uYWwgTmFpdmUiLCAiRHJpZnQiKQoKRnVsbF9HcmFwaCA9IChnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBGaXJzdF9EYXksIG1hcHBpbmcgPSBhZXMgKHggPSBZZWFyLCB5ID0gT3BlbikpICsgICAgICAgICAgICAgCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IEZpcnN0X0RheSwgbWFwcGluZyA9IGFlcyh4ID0gWWVhciwgeSA9IE9wZW4pKSArCiAgICAgIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJTJlAgNTAwIE9wZW4gKCQpIiwgdGl0bGUgPSJTJlAgNTAwIEZpcnN0IE1hcmtldCBPcGVuIG9mIHRoZSBZZWFyIikgKyAKICBnZW9tX2xpbmUoZGF0YSA9IERGX1ByZWRpY3Rpb25zLCBtYXBwaW5nID0gYWVzICh4ID0gWWVhciwgeSA9IE1vdmluZ19BdmVyYWdlKSkgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBERl9QcmVkaWN0aW9ucywgbWFwcGluZyA9IGFlcyAoeCA9IFllYXIsIHkgPSBNb3ZpbmdfQXZlcmFnZSwgY29sb3IgPSAiTW92aW5nIEF2ZXJhZ2UiKSkgKyAKICAKICBnZW9tX2xpbmUoZGF0YSA9IERGX1ByZWRpY3Rpb25zLCBtYXBwaW5nID0gYWVzICh4ID0gWWVhciwgeSA9IE5haXZlKSkgKyAKICAgICAgZ2VvbV9wb2ludChkYXRhID0gREZfUHJlZGljdGlvbnMsIG1hcHBpbmcgPSBhZXMgKHggPSBZZWFyLCB5ID0gTmFpdmUsIGNvbG9yID0gIk5haXZlIikpICsgCiAgCiAgZ2VvbV9saW5lKGRhdGEgPSBERl9QcmVkaWN0aW9ucywgbWFwcGluZz1hZXMoeCA9IFllYXIsIHkgPSBgU2Vhc29uYWwgTmFpdmVgKSkgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBERl9QcmVkaWN0aW9ucywgbWFwcGluZyA9IGFlcyAoeCA9IFllYXIsIHkgPSBTZWFzb25hbF9OYWl2ZSwgY29sb3IgPSAiU2Vhc29uYWwgTmFpdmUiKSkgKyAKICAKICBnZW9tX2xpbmUoZGF0YSA9IERGX1ByZWRpY3Rpb25zLCBtYXBwaW5nPWFlcyh4ID0gWWVhciwgeSA9IGBEcmlmdGApKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IERGX1ByZWRpY3Rpb25zLCBtYXBwaW5nID0gYWVzICh4ID0gWWVhciwgeSA9IERyaWZ0LCBjb2xvciA9ICJEcmlmdCIpKSArCiAgCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgbGltaXRzID0gYygxOTI1LCAyMDIwKSwKICAgIGJyZWFrcyA9IHNlcSgxOTMwLCAyMDIwLCAxMCkgCiAgKSArCgogIHNjYWxlX3lfY29udGludW91cygKICAgIGxpbWl0cyA9IGMoMCwgMzI1MCksCiAgICBicmVha3MgPSBzZXEoMCwgMzUwMCwgNTAwKSAKICApKQoKRnVsbF9HcmFwaAoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpSZWR1Y2VkX0dyYXBoID0gKGdncGxvdCgpICsgIAogIGdlb21fbGluZShkYXRhID0gRmlyc3RfRGF5LCBtYXBwaW5nID0gYWVzICh4ID0gWWVhciwgeSA9IE9wZW4pKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IEZpcnN0X0RheSwgbWFwcGluZyA9IGFlcyh4ID0gWWVhciwgeSA9IE9wZW4pKSArCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIlMmUCA1MDAgT3BlbiAoJCkiLCB0aXRsZSA9IlMmUCA1MDAgRmlyc3QgTWFya2V0IE9wZW4gb2YgdGhlIFllYXIgKDIwMDAgLSAyMDIwKSIpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBERl9QcmVkaWN0aW9ucywgbWFwcGluZyA9IGFlcyAoeCA9IFllYXIsIHkgPSBNb3ZpbmdfQXZlcmFnZSkpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gREZfUHJlZGljdGlvbnMsIG1hcHBpbmcgPSBhZXMgKHggPSBZZWFyLCB5ID0gTW92aW5nX0F2ZXJhZ2UsIGNvbG9yID0gIk1vdmluZyBBdmVyYWdlIiwpKSArIAogIAogIGdlb21fbGluZShkYXRhID0gREZfUHJlZGljdGlvbnMsIG1hcHBpbmcgPSBhZXMgKHggPSBZZWFyLCB5ID0gTmFpdmUpKSArIAogICAgICBnZW9tX3BvaW50KGRhdGEgPSBERl9QcmVkaWN0aW9ucywgbWFwcGluZyA9IGFlcyAoeCA9IFllYXIsIHkgPSBOYWl2ZSwgY29sb3IgPSAiTmFpdmUiKSkgKyAKICAKICBnZW9tX2xpbmUoZGF0YSA9IERGX1ByZWRpY3Rpb25zLCBtYXBwaW5nPWFlcyh4ID0gWWVhciwgeSA9IGBTZWFzb25hbCBOYWl2ZWApKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IERGX1ByZWRpY3Rpb25zLCBtYXBwaW5nID0gYWVzICh4ID0gWWVhciwgeSA9IFNlYXNvbmFsX05haXZlLCBjb2xvciA9ICJTZWFzb25hbCBOYWl2ZSIpKSArIAogIAogIGdlb21fbGluZShkYXRhID0gREZfUHJlZGljdGlvbnMsIG1hcHBpbmc9YWVzKHggPSBZZWFyLCB5ID0gYERyaWZ0YCkpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gREZfUHJlZGljdGlvbnMsIG1hcHBpbmcgPSBhZXMgKHggPSBZZWFyLCB5ID0gRHJpZnQsIGNvbG9yID0gIkRyaWZ0IikpICsKICAKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBsaW1pdHMgPSBjKDIwMDAsIDIwMjApLAogICAgYnJlYWtzID0gc2VxKDIwMDAsIDIwMjAsIDQpIAogICkgKwogIAogIHNjYWxlX3lfY29udGludW91cygKICAgIGxpbWl0cyA9IGMoMjUwLCAzMjUwKSwKICAgYnJlYWtzID0gc2VxKDI1MCwgMzI1MCwgNTAwKQogICkKICApCgpSZWR1Y2VkX0dyYXBoCmBgYApXZSBjYW4gc2VlIGZyb20gb3VyIHR3byBncmFwaHMgdGhhdCwgbGlrZSBzZWVuIGluIHRoZSBzdW1tYXJ5IHRhYmxlIGFib3ZlLCB0aGUgbW92aW5nIGF2ZXJhZ2UgbW9kZWwncyBmb3JlY2FzdHMgYXJlIHdlbGwgYmVuZWF0aCB0aG9zZSBvZiB0aGUgb3RoZXIgdGhyZWUgbW9kZWxzLiBUaGF0IGlzIGJlY2F1c2Ugb2YgdGhlIG11Y2ggZ3JlYXRlciBpbmZsdWVuY2UgdGhhdCB0aGUgZmlyc3Qgcm91Z2hseSA1MCB5ZWFycyBvZiB0aGUgUyZQJ3Mgb3BlbmluZyBtYXJrZXQgcHJpY2UgaGFzIG9uIHRoZSBtb3ZpbmcgYXZlcmFnZSdzIGNhbGN1bGF0aW9uIHRoYW4gaXQgZG9lcyBvbiB0aGUgbmFpdmUsIHNlYXNvbmFsIG5haXZlIG9yIGRyaWZ0LiBUaGUgYXZlcmFnZSBvcGVuaW5nIHByaWNlIGZyb20gMTkyOCB0byAxOTc4IHdhcyBvbmx5IGFib3V0ICRgciByb3VuZChzdW0oVHJhaW5pbmdfRGF0YVsxOjUxLCAyXSkvNTAsIDIpYC4KCkFkZGl0aW9uYWxseSwgd2hpbGUgb3VyIG90aGVyIHRocmVlIG1vZGVscycgZm9yZWNhc3RzIHJlc2VtYmxlIHRoYXQgb2YgdGhlIGluZGV4JyB0cnVlIG9wZW5pbmcgdmFsdWUgZmFyIGdyZWF0ZXIgdGhhbiB0aG9zZSBvZiBvdXIgbW92aW5nIGF2ZXJhZ2UgbW9kZWwsIHRoZXkgYWxzbyBjb25zaXN0ZW50bHkgdW5kZXJlc3RpbWF0ZWQuIFRoZSBkcmlmdCBtb2RlbCdzIHByZWRpY3RlZCB2YWx1ZXMgd2VyZSB0aGUgbW9zdCBhY2N1cmF0ZSwgd2hpY2ggaXMgbm90IHN1cHJpc2luZyBnaXZlbiB0aGF0LCBhcyBwcmV2aW91c2x5IHNhaWQsIHRoZSBkcmlmdCBhcHByb2FjaCBkb2VzIGFjY291bnQgZm9yIGhpc3RvcmljYWwgdHJlbmRzIGluIGluY3JlYXNlIG9yIGRlY3JlYXNlLiBUaGUgcmVhc29uIHRoZSBkcmlmdCBmb3JlY2FzdHMnIHRyZW5kIHdhcyBpbiB0aGUgcHJvcGVyIGRpcmVjdGlvbiAoaW5jcmVhc2luZyBvdmVyIHRpbWUpLCBidXQgYXQgYSByYXRlIGZhciBsZXNzZXIgdGhhbiB0aGUgdHJ1ZSByYXRlIG9mIG1hcmtldCBvcGVuIGluY3JlYXNlIGlzIGR1ZSB0byB0aGUgZXh0cmFvcmRpbmFyeSB3ZWFrbmVzcyBvZiB0aGUgUyZQIG9wZW5pbmcncyB1cHdhcmQgdHJlbmQgdGhyb3VnaG91dCB0aGUgZmlyc3QgZml2ZSBkZWNhZGVzIG9yIHNvIG9mIG91ciBkYXRhc2V0LgoKYGBge3IsIGluY2x1ZGU9RkFMU0V9CiMgQXZlcmFnZSBTJlAgb3BlbmluZyBwcmljZSBmcm9tIDE5MjggLSAxOTc4CnN1bShUcmFpbmluZ19EYXRhWzE6NTEsIDJdKS81MApgYGAKCiMgTW9kZWwgQWNjdXJhY3kgTWVhc3VyZXMKRmluYWxseSwgSSB3YW50ZWQgdG8gcXVhbnRpZnkgdGhlIHN0cmVuZ3RoIG9mIG15IGZvdXIgbW9kZWxzIHVzaW5nIHNvbWUgc3RhbmRhcmQgbWVhc3VyZXMgb2YgbW9kZWxpbmcgYWNjdXJhY3kuIEJlbG93IGlzIGEgYnJlYWtkb3duIG9mIGVhY2ggbW9kZWwncyBtZWFuIGVycm9yIChNRSksIG1lYW4gc3F1YXJlIGVycm9yIChNU0UpIGFuZCBtZWFuIGFic29sdXRlIHByZWRpY3Rpb24gZXJyb3IgKE1BUEUpLiBNZWFuIGVycm9yIGFuZCBtZWFuIHNxdWFyZSBlcnJvciBhcmUgY29uc2lkZXJlZCBtZWFzdXJlcyBvZiBhYnNvbHV0ZSBlcnJvciwgYW5kIGFyZSBhIGdvb2QgcmVmZXJlbmNlIHN0YXRpc3RpYyB0byBzdW1tYXJpemUgaG93ICJvZmYiIG91ciBtb2RlbHMnIGZvcmVjYXN0cyB3ZXJlIG9uIGF2ZXJhZ2UuIE1lYW53aGlsZSwgTUFQRSBpcyBjYWxsZWQgYSBtZWFzdXJlIG9mIHJlbGF0aXZlIGVycm9yLCBhcyBpdCB0ZWxscyB1cyBvbiBhdmVyYWdlIGhvdyAib2ZmIiBvdXIgbW9kZWxzJyBmb3JlY2FzdHMgd2VyZSBhcyBhICpwZXJjZW50Ki4KCk5vbmUgb2YgdGhlIG1ldHJpY3MgY2FsY3VsYXRlZCBpbiB0aGUgdGFibGUgYmVsb3cgYXJlIHBhcnRpY3VsYXJseSBzdXJwcmlzaW5nLiBUaGV5IGFyZSBpbi1saW5lIHdpdGggb3VyIHByZXZpb3VzIGdyYXBoaWNhbCBkaXNwbGF5cyBhbmQgZm9yZWNhc3Qgc3VtbWFyeSB0YWJsZS4gT3VyIGRyaWZ0IG1vZGVsIGlzIHRoZSBtb3N0IGFjY3VyYXRlLCBvdXIgbmFpdmUgYW5kIHNlYXNvbmFsIG5haXZlIG1vZGVscyBhcmUgc2xpZ2h0bHkgd29yc2UsIGFuZCB0aGUgbW92aW5nIGF2ZXJhZ2UgaXMgc2lnbmlmaWNhbnRseSBsZXNzIHVzZWZ1bCB0aGFuIHRoZSBvdGhlcnMuCmBgYHtyfQojIFdpbGwgZmluZCBtZWFuIGVycm9yIChNRSksIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKSBhbmQgbWVhbiBhYnNvbHV0ZSBwcmVkaWN0aW9uIGVycm9yIChNQVBFKSBmb3IgZWFjaCBvZiB0aGUgNCBiYXNlbGluZSBtb2RlbHMuIFRoZW4gY2JpbmQgdGhlbSBhbGwgaW50byBhIHRhYmxlIGFuZCBvdXRwdXQgd2l0aCBrYWJsZSBmdW5jdGlvbi4KCiMjIyBNRSBmaXJzdApNb3ZpbmdfQXZlcmFnZV9FcnJvcnMgPSBkYXRhLmZyYW1lKERGX1ByZWRpY3Rpb25zJGBNb3ZpbmcgQXZlcmFnZWAgLSBUZXN0aW5nX0RhdGEkT3BlbikKICAgIE1vdmluZ19BdmVyYWdlX01FID0gc3VtKE1vdmluZ19BdmVyYWdlX0Vycm9ycykvbnJvdyhNb3ZpbmdfQXZlcmFnZV9FcnJvcnMpCgpOYWl2ZV9FcnJvcnMgPSBkYXRhLmZyYW1lKERGX1ByZWRpY3Rpb25zJE5haXZlIC0gVGVzdGluZ19EYXRhJE9wZW4pCiAgICBOYWl2ZV9NRSA9IHN1bShOYWl2ZV9FcnJvcnMpL25yb3coTmFpdmVfRXJyb3JzKQogICAgClNlYXNvbmFsX05haXZlX0Vycm9ycyA9IGRhdGEuZnJhbWUoREZfUHJlZGljdGlvbnMkYFNlYXNvbmFsIE5haXZlYCAtIFRlc3RpbmdfRGF0YSRPcGVuKQogICAgU2Vhc29uYWxfTmFpdmVfTUUgPSBzdW0oU2Vhc29uYWxfTmFpdmVfRXJyb3JzKS9ucm93KFNlYXNvbmFsX05haXZlX0Vycm9ycykKICAgIApEcmlmdF9FcnJvcnMgPSBkYXRhLmZyYW1lKERGX1ByZWRpY3Rpb25zJERyaWZ0IC0gVGVzdGluZ19EYXRhJE9wZW4pCiAgICBEcmlmdF9NRSA9IHN1bShEcmlmdF9FcnJvcnMpL25yb3coRHJpZnRfRXJyb3JzKQoKQWxsX01vZGVsX01FcyA9IHJiaW5kKE1vdmluZ19BdmVyYWdlX01FLCBOYWl2ZV9NRSwgU2Vhc29uYWxfTmFpdmVfTUUsIERyaWZ0X01FKQoKIyMjIE1TRSBzZWNvbmQKCiMgQ3JlYXRlIHNxdWFyZWQgZXJyb3IgZGF0YSBmcmFtZXMKTW92aW5nX0F2ZXJhZ2VfU1FFcnJvcnMgPSBkYXRhLmZyYW1lKE1vdmluZ19BdmVyYWdlX0Vycm9yc14yKQpOYWl2ZV9TUUVycm9ycyA9IGRhdGEuZnJhbWUoTmFpdmVfRXJyb3JzXjIpClNlYXNvbmFsX05haXZlX1NRRXJyb3JzID0gZGF0YS5mcmFtZShTZWFzb25hbF9OYWl2ZV9FcnJvcnNeMikKRHJpZnRfU1FFcnJvcnMgPSBkYXRhLmZyYW1lKERyaWZ0X0Vycm9yc14yKQoKIyBVc2Ugc3F1YXJlZCBlcnJvciBkYXRhIGZyYW1lcyB0byBjcmVhdGUgTVNFIG9iamVjdHMKTW92aW5nX0F2ZXJhZ2VfTVNFID0gc3VtKE1vdmluZ19BdmVyYWdlX1NRRXJyb3JzKS9ucm93KE1vdmluZ19BdmVyYWdlX1NRRXJyb3JzKQpOYWl2ZV9NU0UgPSBzdW0oTmFpdmVfU1FFcnJvcnMpL25yb3coTmFpdmVfU1FFcnJvcnMpClNlYXNvbmFsX05haXZlX01TRSA9IHN1bShTZWFzb25hbF9OYWl2ZV9TUUVycm9ycykvbnJvdyhTZWFzb25hbF9OYWl2ZV9TUUVycm9ycykKRHJpZnRfTVNFID0gc3VtKERyaWZ0X1NRRXJyb3JzKS9ucm93KERyaWZ0X1NRRXJyb3JzKQoKQWxsX01vZGVsX01TRXMgPSByYmluZChNb3ZpbmdfQXZlcmFnZV9NU0UsIE5haXZlX01TRSwgU2Vhc29uYWxfTmFpdmVfTVNFLCBEcmlmdF9NU0UpCgojTUFQRSB0aGlyZApNb3ZpbmdfQXZlcmFnZV9QRUkgPSBkYXRhLmZyYW1lKChNb3ZpbmdfQXZlcmFnZV9FcnJvcnMvVGVzdGluZ19EYXRhJE9wZW4pKjEwMCkKTW92aW5nX0F2ZXJhZ2VfTUFQRSA9IHJvdW5kKHN1bShhYnMoTW92aW5nX0F2ZXJhZ2VfUEVJKSkvbnJvdyhNb3ZpbmdfQXZlcmFnZV9QRUkpLDQpCgpOYWl2ZV9QRUkgPSBkYXRhLmZyYW1lKChOYWl2ZV9FcnJvcnMvVGVzdGluZ19EYXRhJE9wZW4pKjEwMCkKTmFpdmVfTUFQRSA9IHJvdW5kKHN1bShhYnMoTmFpdmVfUEVJKSkvbnJvdyhOYWl2ZV9QRUkpLDQpCgpTZWFzb25hbF9OYWl2ZV9QRUkgPSBkYXRhLmZyYW1lKChTZWFzb25hbF9OYWl2ZV9FcnJvcnMvVGVzdGluZ19EYXRhJE9wZW4pKjEwMCkKU2Vhc29uYWxfTmFpdmVfTUFQRSA9IHJvdW5kKHN1bShhYnMoU2Vhc29uYWxfTmFpdmVfUEVJKSkvbnJvdyhTZWFzb25hbF9OYWl2ZV9QRUkpLDQpCgpEcmlmdF9QRUkgPSBkYXRhLmZyYW1lKChEcmlmdF9FcnJvcnMvVGVzdGluZ19EYXRhJE9wZW4pKjEwMCkKRHJpZnRfTUFQRSA9IHJvdW5kKHN1bShhYnMoRHJpZnRfUEVJKSkvbnJvdyhEcmlmdF9QRUkpLDQpCgpBbGxfTW9kZWxfTUFQRXMgPSByYmluZChNb3ZpbmdfQXZlcmFnZV9NQVBFLCBOYWl2ZV9NQVBFLCBTZWFzb25hbF9OYWl2ZV9NQVBFLCBEcmlmdF9NQVBFKQoKTW9kZWxfQWNjdXJhY3kgPSBjYmluZChBbGxfTW9kZWxfTUVzLCBBbGxfTW9kZWxfTVNFcywgQWxsX01vZGVsX01BUEVzKQpyb3duYW1lcyhNb2RlbF9BY2N1cmFjeSkgPSBjKCJNb3ZpbmcgQXZlcmFnZSIsICJOYWl2ZSIsICJTZWFzb25hbCBOYWl2ZSIsICJEcmlmdCIpCmNvbG5hbWVzKE1vZGVsX0FjY3VyYWN5KSA9IGMoIk1FIiwgIk1TRSIsICJNQVBFIikKa2FibGUoTW9kZWxfQWNjdXJhY3ksIGNhcHRpb24gPSAiTW9kZWwgQWNjdXJhY3kgU3VtbWFyeSIpCmBgYAoKIyBDb25jbHVzaW9uCkluIGNvbmNsdXNpb24sIHdlIGNhbiBzZWUgdGhhdCB0aGUgc3RyYWlnaHRmb3J3YXJkIGFwcGxpY2F0aW9uIG9mIHRoZXNlIGZvdXIgc3RhbmRhcmQgYW5kIGNvbW1vbiB0aW1lIHNlcmllcyBtb2RlbGluZyBtZXRob2RzIG9uIHRoZSBoaXN0b3JpY2FsIHN0b2NrIG1hcmtldCBkYXRhIHRoYXQgd2FzIGV4YW1pbmVkLCB3ZXJlIG5vdCBwYXJ0aWN1bGFybHkgdXNlZnVsIGluIGZvcmVjYXN0aW5nIGZ1dHVyZSBTJlAgNTAwIG9wZW5pbmcgbWFya2V0IHByaWNlcy4gVGhpcyB3YXMgbGFyZ2VseSBkdWUgdG8gdGhlIGNvbnNpc3RlbnRseSBsb3cgbWFya2V0IG9wZW5zIHRocm91Z2hvdXQgdGhlIGZpcnN0IDUwIG9yIHNvIHllYXJzIG9mIHRoZSBTJlAgaW5kZXguIEluIHRoZSBmdXR1cmUsIGl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIGV4YW1pbmUgdGhlIG1vZGVsIGFjY3VyYWN5IG9mIHRoZXNlIG1ldGhvZHMgLSBwYXJ0aWN1bGFybHkgdGhlIG1vdmluZyBhdmVyYWdlIGFuZCBkcmlmdCAtIGlmIHRoZSByYW5nZSBvZiB0aGUgdHJhaW5pbmcgZGF0YSB3YXMgZ3JlYXRseSByZWR1Y2VkIHRvIG9ubHkgaW5jbHVkZSBtb3JlIHJlY2VudCBkZWNhZGVzLgoKIyBSZWZlcmVuY2VzOgoKT3JpZ2luYWwgRGF0YXNldCBTb3VyY2U6CgotIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvaGVucnloYW4xMTcvc3AtNTAwLWhpc3RvcmljYWwtZGF0YS9kYXRhCgpEYXRhc2V0IERvd25sb2FkIExpbmsgdmlhIEdpdGh1YjoKCi0gaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0NocmlzQjIzMjMvU1RBMzIxL3JlZnMvaGVhZHMvbWFpbi9TUFguY3N2Cg==