library(ggplot2)
library(dplyr)
Warning: package ‘dplyr’ was built under R version 4.4.3
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ lubridate 1.9.4 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.1
✔ readr 2.1.5 ── Conflicts ────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(tsibble)
Registered S3 method overwritten by 'tsibble':
method from
as_tibble.grouped_df dplyr
Attaching package: ‘tsibble’
The following object is masked from ‘package:lubridate’:
interval
The following objects are masked from ‘package:base’:
intersect, setdiff, union
library(lubridate)
library(ggrepel)
library(xts)
Loading required package: zoo
Attaching package: ‘zoo’
The following object is masked from ‘package:tsibble’:
index
The following objects are masked from ‘package:base’:
as.Date, as.Date.numeric
######################### Warning from 'xts' package ##########################
# #
# The dplyr lag() function breaks how base R's lag() function is supposed to #
# work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or #
# source() into this session won't work correctly. #
# #
# Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
# conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop #
# dplyr from breaking base R's lag() function. #
# #
# Code in packages is not affected. It's protected by R's namespace mechanism #
# Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning. #
# #
###############################################################################
Attaching package: ‘xts’
The following objects are masked from ‘package:dplyr’:
first, last
# Install the pageviews package
install.packages("pageviews")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:
https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.4/pageviews_0.6.0.zip'
Content type 'application/zip' length 37973 bytes (37 KB)
downloaded 37 KB
package ‘pageviews’ successfully unpacked and MD5 sums checked
The downloaded binary packages are in
C:\Users\dsjja\AppData\Local\Temp\Rtmps9iDdF\downloaded_packages
# Load the library
library(pageviews)
Warning: package ‘pageviews’ was built under R version 4.4.3
#Reading the data set
data <- read.csv("dataset.csv")
conflicted::conflicts_prefer(dplyr::filter)
[conflicted] Will prefer dplyr::filter over any other package.
# Filtering dataset where explicit is "True" and taking a sample of 9,000 rows
sample_data <- data |> filter(explicit == "True") |> sample_n(9000)
data <- sample_data
data
names(data)
[1] "X" "track_id" "artists" "album_name" "track_name" "popularity"
[7] "duration_ms" "explicit" "danceability" "energy" "key" "loudness"
[13] "mode" "speechiness" "acousticness" "instrumentalness" "liveness" "valence"
[19] "tempo" "time_signature" "track_genre"
Select a column of your data that encodes time (e.g., “date”,
“timestamp”, “year”, etc.). Convert this into a Date in R. If you do not
have a time-based column of data: find a Wikipedia page that is related
to your dataset. Then, extract a time series of page views for that page
using the wikipedia page views websiteLinks to an external site. or the
R package used in this week’s lab.
# Extract daily page views for "Spotify" Wikipedia page
spotify_views <- article_pageviews(
project = "en.wikipedia",
article = "Spotify",
start = as.Date("2023-01-01"),
end = as.Date("2023-12-31"),
user_type = "user", # Only user traffic
platform = "all" # All platforms: desktop, mobile-web, etc.
)
# View the first few rows
head(spotify_views)
# Plot the time series of page views
ggplot(spotify_views, aes(x = date, y = views)) +
geom_line(color = "steelblue", size = 1) +
labs(title = "Daily Wikipedia Page Views for 'Spotify' in 2023",
x = "Date",
y = "Views") +
theme_minimal()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

nrow(data)
[1] 9000
nrow(spotify_views)
[1] 365
# View the first few rows to understand the structure of the data
head(spotify_views)
Choose a column of data to analyze over time. This should be a
“response-like” variable that is of particular interest.
I choosed popularity.
Create a tsibble object of just the date and response variable.
Then, plot your data over time. Consider different windows of time.
library(tsibble)
# Ensure the date column is in Date format
spotify_views$date <- as.Date(spotify_views$date)
# Create a tsibble object with date as the index and views as the response variable
spotify_ts <- spotify_views |>
select(date, views) |>
rename(popularity = views) |>
as_tsibble(index = date)
# View the time series object
head(spotify_ts)
NA
# Plot popularity over time
ggplot(spotify_ts, aes(x = date, y = popularity)) +
geom_line(color = "steelblue", linewidth = 1) +
labs(
title = "Spotify Track Popularity Over Time (Based on Wikipedia Views)",
x = "Date",
y = "Popularity (Views)"
) + theme_minimal()

# Aggregate by week
weekly_ts <- spotify_ts |>
index_by(week = ~ lubridate::floor_date(., "week")) |>
summarise(mean_popularity = mean(popularity, na.rm = TRUE))
# Plot
ggplot(weekly_ts, aes(x = week, y = mean_popularity)) +
geom_line(color = "darkgreen") +
labs(title = "Weekly Average Popularity", x = "Week", y = "Avg Views") +
theme_minimal()

# By month
monthly_ts <- spotify_ts |>
index_by(month = ~ lubridate::floor_date(., "month")) |>
summarise(avg_popularity = mean(popularity, na.rm = TRUE))
ggplot(monthly_ts, aes(x = month, y = avg_popularity)) +
geom_line(color = "orange") +
labs(title = "Monthly Trends in Popularity", x = "Month", y = "Average Views") +
theme_minimal()

Use linear regression to detect any upwards or downwards
trends.
# Fit the linear regression model
model <- lm(popularity ~ date, data = spotify_ts)
summary(model)
Call:
lm(formula = popularity ~ date, data = spotify_ts)
Residuals:
Min 1Q Median 3Q Max
-2778.0 -842.5 -9.7 553.9 6952.8
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.410e+05 1.249e+04 19.29 <2e-16 ***
date -1.200e+01 6.393e-01 -18.77 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1287 on 363 degrees of freedom
Multiple R-squared: 0.4927, Adjusted R-squared: 0.4913
F-statistic: 352.5 on 1 and 363 DF, p-value: < 2.2e-16
ggplot(spotify_ts, aes(x = date, y = popularity)) +
geom_point(alpha = 0.5) +
geom_smooth(method = "lm", se = TRUE, color = "blue") +
labs(title = "Trend of Popularity over Date",
x = "Date", y = "Popularity") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Based on the graph showing the “Trend of Popularity over Date” from
January 2023 to January 2024, Data Trend Analysis:
The graph displays a clear overall downward trend in popularity over
the one-year period, with the smoothed blue line showing a decline from
approximately 8,500 in January 2023 to around 4,500 by January 2024.
This represents nearly a 50% decrease in popularity over the year.
Do you need to subset the data for multiple trends?
Yes, subsetting the data would be beneficial for several reasons:
There appear to be distinct clusters in the data that suggest
different patterns within the overall trend
The first quarter of 2023 shows significantly higher popularity
values (10,000-13,000 range) compared to other periods
There’s a visible increase in variability and some higher points
again in late 2023/early 2024
The simple linear trend line (using the formula ‘y ~ x’) doesn’t
capture these potential cyclical or seasonal patterns
How strong are these trends?
The overall downward trend appears moderately strong, evidenced
by:
The consistent negative slope of the blue trend line throughout the
entire period
The relatively narrow confidence interval (blue shaded area) around
the trend line, suggesting statistical significance
However, the substantial scatter of data points around the trend line
indicates high variability
The graph shows considerable dispersion of data points, with many
falling far from the trend line. This suggests that while the downward
trend is clear, it explains only a portion of the variation in
popularity. The high degree of scatter indicates that other factors
beyond the simple time variable are likely influencing popularity
values.
Use smoothing to detect at least one season in your data, and
interpret your results.
spotify_ts |> ggplot(mapping = aes(x = date, y = popularity)) +
geom_point(size = 1, alpha = 0.4) +
geom_smooth(span = 0.2, color = 'blue', se = FALSE)+theme_classic()

Interpretation of the Smoothing Result
The plot uses LOESS smoothing (with
span = 0.2
) to visualize the trend in the “popularity”
variable over time (from January 2023 to January 2024).
Key Observations
- Seasonal Pattern Detected:
- There is a clear seasonal trend in the data.
- Early 2023: Popularity starts high, peaking around
January.
- Spring 2023: There is a sharp
decline in popularity from January to around April.
- Mid-2023: Popularity remains relatively low and
stable, with minor fluctuations.
- Late 2023: There are small increases and decreases,
but no major spikes until a slight uptick at the end of the year.
- Possible Seasonality:
- The initial peak and subsequent drop suggest a
seasonal effect—possibly related to an event or release
that caused a spike in popularity at the start of the year.
- The smaller oscillations throughout the rest of the
year may indicate minor seasonal or periodic effects,
but they are less pronounced than the initial drop.
- Noise and Outliers:
- The scatterplot shows some outliers (especially
high values) that the smoother does not follow closely, which is
expected since the smoother is designed to capture the general trend,
not every fluctuation.
Can you illustrate the seasonality using ACF or PACF?
acf(spotify_ts, ci = 0.95, na.action = na.exclude)

This ACF plot for spotify_ts suggests the series is not strongly
seasonal but may have a trend or be non-stationary.
pacf(spotify_ts, na.action = na.exclude, xlab = 'lag', main = "PACF for Spotify pageviews" )

This PACF plot for Spotify pageviews shows a strong correlation at
lag 1 and little to no significant correlation at higher lags. This
suggests that an AR(1) model could be a good starting point for modeling
this time series analysis.
To detect seasonality in my data, I used LOESS smoothing with a span
of 0.2, as shown in the plot. Here’s what I observed:
Clear Seasonality: At the beginning of 2023, there is a noticeable
peak in popularity, with values above 10,000. This suggests a strong
seasonal effect or a specific event that drove popularity up during this
period.
Sharp Decline and Stabilization: After this initial peak, popularity
drops sharply through the first quarter of the year, reaching a low
around April 2023. From that point onward, the popularity remains
relatively stable, fluctuating between 5,000 and 7,000, with only minor
ups and downs.
Minor Fluctuations: Throughout the rest of the year, I noticed some
smaller oscillations, but none are as dramatic as the initial drop. This
indicates that while there may be some minor seasonal effects, the main
seasonality is concentrated at the start of the year.
Outliers: There are a few outlier points, especially high values,
that the smoother doesn’t follow closely. This is expected, as the
smoothing method is designed to capture the overall trend rather than
every individual fluctuation.
Conclusion:
From this analysis, I can conclude that there is at least one strong
season in my data, with a major peak in popularity at the start of the
year, followed by a sharp decline and a stable period. This suggests
that timing plays a significant role in popularity, and it may be
beneficial to align major releases or promotions with the period where
the peak occurs.
If I want to understand the causes behind these trends more deeply, I
could further investigate what happened during the peak period or use
more advanced time series analysis techniques.
LS0tDQp0aXRsZTogIldlZWsgLSAxMiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KHh0cykNCmBgYA0KDQoNCmBgYHtyfQ0KIyBJbnN0YWxsIHRoZSBwYWdldmlld3MgcGFja2FnZQ0KaW5zdGFsbC5wYWNrYWdlcygicGFnZXZpZXdzIikNCg0KIyBMb2FkIHRoZSBsaWJyYXJ5DQpsaWJyYXJ5KHBhZ2V2aWV3cykNCmBgYA0KDQpgYGB7cn0NCiNSZWFkaW5nIHRoZSBkYXRhIHNldA0KZGF0YSA8LSByZWFkLmNzdigiZGF0YXNldC5jc3YiKQ0KY29uZmxpY3RlZDo6Y29uZmxpY3RzX3ByZWZlcihkcGx5cjo6ZmlsdGVyKQ0KIyBGaWx0ZXJpbmcgZGF0YXNldCB3aGVyZSBleHBsaWNpdCBpcyAiVHJ1ZSIgYW5kIHRha2luZyBhIHNhbXBsZSBvZiA5LDAwMCByb3dzDQpzYW1wbGVfZGF0YSA8LSBkYXRhIHw+IGZpbHRlcihleHBsaWNpdCA9PSAiVHJ1ZSIpIHw+IHNhbXBsZV9uKDkwMDApDQpkYXRhIDwtIHNhbXBsZV9kYXRhDQpkYXRhDQpgYGANCmBgYHtyfQ0KbmFtZXMoZGF0YSkNCmBgYA0KIyMjIFNlbGVjdCBhIGNvbHVtbiBvZiB5b3VyIGRhdGEgdGhhdCBlbmNvZGVzIHRpbWUgKGUuZy4sICJkYXRlIiwgInRpbWVzdGFtcCIsICJ5ZWFyIiwgZXRjLikuIENvbnZlcnQgdGhpcyBpbnRvIGEgRGF0ZSBpbiBSLiBJZiB5b3UgZG8gbm90IGhhdmUgYSB0aW1lLWJhc2VkIGNvbHVtbiBvZiBkYXRhOiBmaW5kIGEgV2lraXBlZGlhIHBhZ2UgdGhhdCBpcyByZWxhdGVkIHRvIHlvdXIgZGF0YXNldC4gVGhlbiwgZXh0cmFjdCBhIHRpbWUgc2VyaWVzIG9mIHBhZ2Ugdmlld3MgZm9yIHRoYXQgcGFnZSB1c2luZyB0aGUgd2lraXBlZGlhIHBhZ2Ugdmlld3Mgd2Vic2l0ZUxpbmtzIHRvIGFuIGV4dGVybmFsIHNpdGUuIG9yIHRoZSBSIHBhY2thZ2UgdXNlZCBpbiB0aGlzIHdlZWsncyBsYWIuDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IGRhaWx5IHBhZ2Ugdmlld3MgZm9yICJTcG90aWZ5IiBXaWtpcGVkaWEgcGFnZQ0Kc3BvdGlmeV92aWV3cyA8LSBhcnRpY2xlX3BhZ2V2aWV3cygNCiAgcHJvamVjdCA9ICJlbi53aWtpcGVkaWEiLA0KICBhcnRpY2xlID0gIlNwb3RpZnkiLA0KICBzdGFydCA9IGFzLkRhdGUoIjIwMjMtMDEtMDEiKSwNCiAgZW5kID0gYXMuRGF0ZSgiMjAyMy0xMi0zMSIpLA0KICB1c2VyX3R5cGUgPSAidXNlciIsICAgICAjIE9ubHkgdXNlciB0cmFmZmljIA0KICBwbGF0Zm9ybSA9ICJhbGwiICAgICAgICAjIEFsbCBwbGF0Zm9ybXM6IGRlc2t0b3AsIG1vYmlsZS13ZWIsIGV0Yy4NCikNCg0KIyBWaWV3IHRoZSBmaXJzdCBmZXcgcm93cw0KaGVhZChzcG90aWZ5X3ZpZXdzKQ0KYGBgDQoNCg0KYGBge3J9DQojIFBsb3QgdGhlIHRpbWUgc2VyaWVzIG9mIHBhZ2Ugdmlld3MNCmdncGxvdChzcG90aWZ5X3ZpZXdzLCBhZXMoeCA9IGRhdGUsIHkgPSB2aWV3cykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gInN0ZWVsYmx1ZSIsIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiRGFpbHkgV2lraXBlZGlhIFBhZ2UgVmlld3MgZm9yICdTcG90aWZ5JyBpbiAyMDIzIiwNCiAgICAgICB4ID0gIkRhdGUiLA0KICAgICAgIHkgPSAiVmlld3MiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3J9DQpucm93KGRhdGEpDQpucm93KHNwb3RpZnlfdmlld3MpDQpgYGANCg0KYGBge3J9DQojIFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIHRvIHVuZGVyc3RhbmQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YQ0KaGVhZChzcG90aWZ5X3ZpZXdzKQ0KYGBgDQojIyMgQ2hvb3NlIGEgY29sdW1uIG9mIGRhdGEgdG8gYW5hbHl6ZSBvdmVyIHRpbWUuIFRoaXMgc2hvdWxkIGJlIGEgInJlc3BvbnNlLWxpa2UiIHZhcmlhYmxlIHRoYXQgaXMgb2YgcGFydGljdWxhciBpbnRlcmVzdC4NCkkgY2hvb3NlZCAqKnBvcHVsYXJpdHkqKi4NCg0KIyMjIENyZWF0ZSBhIHRzaWJibGUgb2JqZWN0IG9mIGp1c3QgdGhlIGRhdGUgYW5kIHJlc3BvbnNlIHZhcmlhYmxlLiBUaGVuLCBwbG90IHlvdXIgZGF0YSBvdmVyIHRpbWUuIENvbnNpZGVyIGRpZmZlcmVudCB3aW5kb3dzIG9mIHRpbWUuDQpgYGB7cn0NCmxpYnJhcnkodHNpYmJsZSkNCg0KIyBFbnN1cmUgdGhlIGRhdGUgY29sdW1uIGlzIGluIERhdGUgZm9ybWF0DQpzcG90aWZ5X3ZpZXdzJGRhdGUgPC0gYXMuRGF0ZShzcG90aWZ5X3ZpZXdzJGRhdGUpDQoNCiMgQ3JlYXRlIGEgdHNpYmJsZSBvYmplY3Qgd2l0aCBkYXRlIGFzIHRoZSBpbmRleCBhbmQgdmlld3MgYXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlDQpzcG90aWZ5X3RzIDwtIHNwb3RpZnlfdmlld3MgfD4NCiAgc2VsZWN0KGRhdGUsIHZpZXdzKSB8Pg0KICByZW5hbWUocG9wdWxhcml0eSA9IHZpZXdzKSB8Pg0KICBhc190c2liYmxlKGluZGV4ID0gZGF0ZSkNCg0KIyBWaWV3IHRoZSB0aW1lIHNlcmllcyBvYmplY3QNCmhlYWQoc3BvdGlmeV90cykNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgUGxvdCBwb3B1bGFyaXR5IG92ZXIgdGltZQ0KZ2dwbG90KHNwb3RpZnlfdHMsIGFlcyh4ID0gZGF0ZSwgeSA9IHBvcHVsYXJpdHkpKSArDQogIGdlb21fbGluZShjb2xvciA9ICJzdGVlbGJsdWUiLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiU3BvdGlmeSBUcmFjayBQb3B1bGFyaXR5IE92ZXIgVGltZSAoQmFzZWQgb24gV2lraXBlZGlhIFZpZXdzKSIsDQogICAgeCA9ICJEYXRlIiwNCiAgICB5ID0gIlBvcHVsYXJpdHkgKFZpZXdzKSINCiAgKSArIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3J9DQojIEFnZ3JlZ2F0ZSBieSB3ZWVrDQp3ZWVrbHlfdHMgPC0gc3BvdGlmeV90cyB8Pg0KICBpbmRleF9ieSh3ZWVrID0gfiBsdWJyaWRhdGU6OmZsb29yX2RhdGUoLiwgIndlZWsiKSkgfD4NCiAgc3VtbWFyaXNlKG1lYW5fcG9wdWxhcml0eSA9IG1lYW4ocG9wdWxhcml0eSwgbmEucm0gPSBUUlVFKSkNCg0KIyBQbG90DQpnZ3Bsb3Qod2Vla2x5X3RzLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3BvcHVsYXJpdHkpKSArDQogIGdlb21fbGluZShjb2xvciA9ICJkYXJrZ3JlZW4iKSArDQogIGxhYnModGl0bGUgPSAiV2Vla2x5IEF2ZXJhZ2UgUG9wdWxhcml0eSIsIHggPSAiV2VlayIsIHkgPSAiQXZnIFZpZXdzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQpgYGB7cn0NCiMgQnkgbW9udGgNCm1vbnRobHlfdHMgPC0gc3BvdGlmeV90cyB8Pg0KICBpbmRleF9ieShtb250aCA9IH4gbHVicmlkYXRlOjpmbG9vcl9kYXRlKC4sICJtb250aCIpKSB8Pg0KICBzdW1tYXJpc2UoYXZnX3BvcHVsYXJpdHkgPSBtZWFuKHBvcHVsYXJpdHksIG5hLnJtID0gVFJVRSkpDQoNCmdncGxvdChtb250aGx5X3RzLCBhZXMoeCA9IG1vbnRoLCB5ID0gYXZnX3BvcHVsYXJpdHkpKSArDQogIGdlb21fbGluZShjb2xvciA9ICJvcmFuZ2UiKSArDQogIGxhYnModGl0bGUgPSAiTW9udGhseSBUcmVuZHMgaW4gUG9wdWxhcml0eSIsIHggPSAiTW9udGgiLCB5ID0gIkF2ZXJhZ2UgVmlld3MiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KIyBXaGF0IHN0YW5kcyBvdXQgaW1tZWRpYXRlbHk/DQpTdGF0aXN0aWNhbCBTaWduaWZpY2FuY2U6IFRoZSBjb25zaXN0ZW5jeSBvZiB0aGUgZG93bndhcmQgdHJlbmQgYWNyb3NzIGRpZmZlcmVudCB0aW1lIGFnZ3JlZ2F0aW9ucyAoZGFpbHksIHdlZWtseSwgbW9udGhseSkgc3Ryb25nbHkgc3VnZ2VzdHMgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIGluIHRoZSBvdmVyYWxsIGRlY2xpbmUsIHBhcnRpY3VsYXJseSBmb3IgdGhlIG1vbnRobHkgZGF0YSB3aGVyZSB0aGUgdHJlbmQgYXBwZWFycyBzdHJvbmdlc3QgKGFzIG5vaXNlIGlzIHJlZHVjZWQgdGhyb3VnaCBhZ2dyZWdhdGlvbikuDQoNClByYWN0aWNhbCBJbXBsaWNhdGlvbnM6IFRoaXMgYW5hbHlzaXMgc3VnZ2VzdHMgdGhhdCBTcG90aWZ5IHRyYWNrIHBvcHVsYXJpdHkgKGFzIG1lYXN1cmVkIGJ5IFdpa2lwZWRpYSB2aWV3cykgZXhwZXJpZW5jZWQgYSBmdW5kYW1lbnRhbCBzaGlmdCBkb3dud2FyZCBpbiBlYXJseSAyMDIzLCBwb3RlbnRpYWxseSBpbmRpY2F0aW5nOiBBIG1ham9yIGNoYW5nZSBpbiB1c2VyIGJlaGF2aW9yLCBQbGF0Zm9ybSBhbGdvcml0aG0gY2hhbmdlcywgQ29tcGV0aXRpb24gZnJvbSBvdGhlciBtdXNpYyBzZXJ2aWNlcywgU2Vhc29uYWwgZWZmZWN0cyBlYXJseSBpbiB0aGUgeWVhciB0aGF0IGRpZG4ndCByZXBlYXQuDQoNCg0KIyBVc2UgbGluZWFyIHJlZ3Jlc3Npb24gdG8gZGV0ZWN0IGFueSB1cHdhcmRzIG9yIGRvd253YXJkcyB0cmVuZHMuDQoNCmBgYHtyfQ0KIyBGaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQptb2RlbCA8LSBsbShwb3B1bGFyaXR5IH4gZGF0ZSwgZGF0YSA9IHNwb3RpZnlfdHMpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KHNwb3RpZnlfdHMsIGFlcyh4ID0gZGF0ZSwgeSA9IHBvcHVsYXJpdHkpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlRyZW5kIG9mIFBvcHVsYXJpdHkgb3ZlciBEYXRlIiwgDQogICAgICAgeCA9ICJEYXRlIiwgeSA9ICJQb3B1bGFyaXR5IikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpgYGANCkJhc2VkIG9uIHRoZSBncmFwaCBzaG93aW5nIHRoZSAiVHJlbmQgb2YgUG9wdWxhcml0eSBvdmVyIERhdGUiIGZyb20gSmFudWFyeSAyMDIzIHRvIEphbnVhcnkgMjAyNCwgRGF0YSBUcmVuZCBBbmFseXNpczoNCg0KVGhlIGdyYXBoIGRpc3BsYXlzIGEgY2xlYXIgb3ZlcmFsbCBkb3dud2FyZCB0cmVuZCBpbiBwb3B1bGFyaXR5IG92ZXIgdGhlIG9uZS15ZWFyIHBlcmlvZCwgd2l0aCB0aGUgc21vb3RoZWQgYmx1ZSBsaW5lIHNob3dpbmcgYSBkZWNsaW5lIGZyb20gYXBwcm94aW1hdGVseSA4LDUwMCBpbiBKYW51YXJ5IDIwMjMgdG8gYXJvdW5kIDQsNTAwIGJ5IEphbnVhcnkgMjAyNC4gVGhpcyByZXByZXNlbnRzIG5lYXJseSBhIDUwJSBkZWNyZWFzZSBpbiBwb3B1bGFyaXR5IG92ZXIgdGhlIHllYXIuDQoNCg0KIyBEbyB5b3UgbmVlZCB0byBzdWJzZXQgdGhlIGRhdGEgZm9yIG11bHRpcGxlIHRyZW5kcz8NCg0KWWVzLCBzdWJzZXR0aW5nIHRoZSBkYXRhIHdvdWxkIGJlIGJlbmVmaWNpYWwgZm9yIHNldmVyYWwgcmVhc29uczoNCg0KVGhlcmUgYXBwZWFyIHRvIGJlIGRpc3RpbmN0IGNsdXN0ZXJzIGluIHRoZSBkYXRhIHRoYXQgc3VnZ2VzdCBkaWZmZXJlbnQgcGF0dGVybnMgd2l0aGluIHRoZSBvdmVyYWxsIHRyZW5kDQoNClRoZSBmaXJzdCBxdWFydGVyIG9mIDIwMjMgc2hvd3Mgc2lnbmlmaWNhbnRseSBoaWdoZXIgcG9wdWxhcml0eSB2YWx1ZXMgKDEwLDAwMC0xMywwMDAgcmFuZ2UpIGNvbXBhcmVkIHRvIG90aGVyIHBlcmlvZHMNCg0KVGhlcmUncyBhIHZpc2libGUgaW5jcmVhc2UgaW4gdmFyaWFiaWxpdHkgYW5kIHNvbWUgaGlnaGVyIHBvaW50cyBhZ2FpbiBpbiBsYXRlIDIwMjMvZWFybHkgMjAyNA0KDQpUaGUgc2ltcGxlIGxpbmVhciB0cmVuZCBsaW5lICh1c2luZyB0aGUgZm9ybXVsYSAneSB+IHgnKSBkb2Vzbid0IGNhcHR1cmUgdGhlc2UgcG90ZW50aWFsIGN5Y2xpY2FsIG9yIHNlYXNvbmFsIHBhdHRlcm5zDQoNCiMgSG93IHN0cm9uZyBhcmUgdGhlc2UgdHJlbmRzPw0KDQpUaGUgb3ZlcmFsbCBkb3dud2FyZCB0cmVuZCBhcHBlYXJzIG1vZGVyYXRlbHkgc3Ryb25nLCBldmlkZW5jZWQgYnk6DQoNClRoZSBjb25zaXN0ZW50IG5lZ2F0aXZlIHNsb3BlIG9mIHRoZSBibHVlIHRyZW5kIGxpbmUgdGhyb3VnaG91dCB0aGUgZW50aXJlIHBlcmlvZA0KDQpUaGUgcmVsYXRpdmVseSBuYXJyb3cgY29uZmlkZW5jZSBpbnRlcnZhbCAoYmx1ZSBzaGFkZWQgYXJlYSkgYXJvdW5kIHRoZSB0cmVuZCBsaW5lLCBzdWdnZXN0aW5nIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQ0KDQpIb3dldmVyLCB0aGUgc3Vic3RhbnRpYWwgc2NhdHRlciBvZiBkYXRhIHBvaW50cyBhcm91bmQgdGhlIHRyZW5kIGxpbmUgaW5kaWNhdGVzIGhpZ2ggdmFyaWFiaWxpdHkNCg0KVGhlIGdyYXBoIHNob3dzIGNvbnNpZGVyYWJsZSBkaXNwZXJzaW9uIG9mIGRhdGEgcG9pbnRzLCB3aXRoIG1hbnkgZmFsbGluZyBmYXIgZnJvbSB0aGUgdHJlbmQgbGluZS4gVGhpcyBzdWdnZXN0cyB0aGF0IHdoaWxlIHRoZSBkb3dud2FyZCB0cmVuZCBpcyBjbGVhciwgaXQgZXhwbGFpbnMgb25seSBhIHBvcnRpb24gb2YgdGhlIHZhcmlhdGlvbiBpbiBwb3B1bGFyaXR5LiBUaGUgaGlnaCBkZWdyZWUgb2Ygc2NhdHRlciBpbmRpY2F0ZXMgdGhhdCBvdGhlciBmYWN0b3JzIGJleW9uZCB0aGUgc2ltcGxlIHRpbWUgdmFyaWFibGUgYXJlIGxpa2VseSBpbmZsdWVuY2luZyBwb3B1bGFyaXR5IHZhbHVlcy4NCg0KDQojIFVzZSBzbW9vdGhpbmcgdG8gZGV0ZWN0IGF0IGxlYXN0IG9uZSBzZWFzb24gaW4geW91ciBkYXRhLCBhbmQgaW50ZXJwcmV0IHlvdXIgcmVzdWx0cy4NCg0KYGBge3J9DQpzcG90aWZ5X3RzIHw+IGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBkYXRlLCB5ID0gcG9wdWxhcml0eSkpICsgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGFscGhhID0gMC40KSArIA0KICBnZW9tX3Ntb290aChzcGFuID0gMC4yLCBjb2xvciA9ICdibHVlJywgc2UgPSBGQUxTRSkrdGhlbWVfY2xhc3NpYygpDQpgYGANCiMjIEludGVycHJldGF0aW9uIG9mIHRoZSBTbW9vdGhpbmcgUmVzdWx0DQoNClRoZSBwbG90IHVzZXMgKipMT0VTUyBzbW9vdGhpbmcqKiAod2l0aCBgc3BhbiA9IDAuMmApIHRvIHZpc3VhbGl6ZSB0aGUgdHJlbmQgaW4gdGhlICJwb3B1bGFyaXR5IiB2YXJpYWJsZSBvdmVyIHRpbWUgKGZyb20gSmFudWFyeSAyMDIzIHRvIEphbnVhcnkgMjAyNCkuDQoNCiMjIyBLZXkgT2JzZXJ2YXRpb25zDQoNCjEuICoqU2Vhc29uYWwgUGF0dGVybiBEZXRlY3RlZDoqKg0KICAgLSBUaGVyZSBpcyBhICoqY2xlYXIgc2Vhc29uYWwgdHJlbmQqKiBpbiB0aGUgZGF0YS4NCiAgIC0gKipFYXJseSAyMDIzOioqIFBvcHVsYXJpdHkgc3RhcnRzIGhpZ2gsIHBlYWtpbmcgYXJvdW5kIEphbnVhcnkuDQogICAtICoqU3ByaW5nIDIwMjM6KiogVGhlcmUgaXMgYSAqKnNoYXJwIGRlY2xpbmUqKiBpbiBwb3B1bGFyaXR5IGZyb20gSmFudWFyeSB0byBhcm91bmQgQXByaWwuDQogICAtICoqTWlkLTIwMjM6KiogUG9wdWxhcml0eSByZW1haW5zIHJlbGF0aXZlbHkgbG93IGFuZCBzdGFibGUsIHdpdGggbWlub3IgZmx1Y3R1YXRpb25zLg0KICAgLSAqKkxhdGUgMjAyMzoqKiBUaGVyZSBhcmUgc21hbGwgaW5jcmVhc2VzIGFuZCBkZWNyZWFzZXMsIGJ1dCBubyBtYWpvciBzcGlrZXMgdW50aWwgYSBzbGlnaHQgdXB0aWNrIGF0IHRoZSBlbmQgb2YgdGhlIHllYXIuDQoNCjIuICoqUG9zc2libGUgU2Vhc29uYWxpdHk6KioNCiAgIC0gVGhlICoqaW5pdGlhbCBwZWFrKiogYW5kIHN1YnNlcXVlbnQgZHJvcCBzdWdnZXN0IGEgKipzZWFzb25hbCBlZmZlY3QqKuKAlHBvc3NpYmx5IHJlbGF0ZWQgdG8gYW4gZXZlbnQgb3IgcmVsZWFzZSB0aGF0IGNhdXNlZCBhIHNwaWtlIGluIHBvcHVsYXJpdHkgYXQgdGhlIHN0YXJ0IG9mIHRoZSB5ZWFyLg0KICAgLSBUaGUgKipzbWFsbGVyIG9zY2lsbGF0aW9ucyoqIHRocm91Z2hvdXQgdGhlIHJlc3Qgb2YgdGhlIHllYXIgbWF5IGluZGljYXRlICoqbWlub3Igc2Vhc29uYWwgb3IgcGVyaW9kaWMgZWZmZWN0cyoqLCBidXQgdGhleSBhcmUgbGVzcyBwcm9ub3VuY2VkIHRoYW4gdGhlIGluaXRpYWwgZHJvcC4NCg0KMy4gKipOb2lzZSBhbmQgT3V0bGllcnM6KioNCiAgIC0gVGhlIHNjYXR0ZXJwbG90IHNob3dzIHNvbWUgKipvdXRsaWVycyoqIChlc3BlY2lhbGx5IGhpZ2ggdmFsdWVzKSB0aGF0IHRoZSBzbW9vdGhlciBkb2VzIG5vdCBmb2xsb3cgY2xvc2VseSwgd2hpY2ggaXMgZXhwZWN0ZWQgc2luY2UgdGhlIHNtb290aGVyIGlzIGRlc2lnbmVkIHRvIGNhcHR1cmUgdGhlIGdlbmVyYWwgdHJlbmQsIG5vdCBldmVyeSBmbHVjdHVhdGlvbi4NCg0KDQojIENhbiB5b3UgaWxsdXN0cmF0ZSB0aGUgc2Vhc29uYWxpdHkgdXNpbmcgQUNGIG9yIFBBQ0Y/DQoNCmBgYHtyfQ0KYWNmKHNwb3RpZnlfdHMsIGNpID0gMC45NSwgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSkNCmBgYA0KVGhpcyBBQ0YgcGxvdCBmb3Igc3BvdGlmeV90cyBzdWdnZXN0cyB0aGUgc2VyaWVzIGlzIG5vdCBzdHJvbmdseSBzZWFzb25hbCBidXQgbWF5IGhhdmUgYSB0cmVuZCBvciBiZSBub24tc3RhdGlvbmFyeS4gDQoNCmBgYHtyfQ0KcGFjZihzcG90aWZ5X3RzLCBuYS5hY3Rpb24gPSBuYS5leGNsdWRlLCB4bGFiID0gJ2xhZycsIG1haW4gPSAiUEFDRiBmb3IgU3BvdGlmeSBwYWdldmlld3MiICkNCmBgYA0KVGhpcyBQQUNGIHBsb3QgZm9yIFNwb3RpZnkgcGFnZXZpZXdzIHNob3dzIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGF0IGxhZyAxIGFuZCBsaXR0bGUgdG8gbm8gc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gYXQgaGlnaGVyIGxhZ3MuIFRoaXMgc3VnZ2VzdHMgdGhhdCBhbiBBUigxKSBtb2RlbCBjb3VsZCBiZSBhIGdvb2Qgc3RhcnRpbmcgcG9pbnQgZm9yIG1vZGVsaW5nIHRoaXMgdGltZSBzZXJpZXMgYW5hbHlzaXMuDQoNCg0KDQoNClRvIGRldGVjdCBzZWFzb25hbGl0eSBpbiBteSBkYXRhLCBJIHVzZWQgTE9FU1Mgc21vb3RoaW5nIHdpdGggYSBzcGFuIG9mIDAuMiwgYXMgc2hvd24gaW4gdGhlIHBsb3QuIEhlcmXigJlzIHdoYXQgSSBvYnNlcnZlZDoNCg0KQ2xlYXIgU2Vhc29uYWxpdHk6DQpBdCB0aGUgYmVnaW5uaW5nIG9mIDIwMjMsIHRoZXJlIGlzIGEgbm90aWNlYWJsZSBwZWFrIGluIHBvcHVsYXJpdHksIHdpdGggdmFsdWVzIGFib3ZlIDEwLDAwMC4gVGhpcyBzdWdnZXN0cyBhIHN0cm9uZyBzZWFzb25hbCBlZmZlY3Qgb3IgYSBzcGVjaWZpYyBldmVudCB0aGF0IGRyb3ZlIHBvcHVsYXJpdHkgdXAgZHVyaW5nIHRoaXMgcGVyaW9kLg0KDQpTaGFycCBEZWNsaW5lIGFuZCBTdGFiaWxpemF0aW9uOg0KQWZ0ZXIgdGhpcyBpbml0aWFsIHBlYWssIHBvcHVsYXJpdHkgZHJvcHMgc2hhcnBseSB0aHJvdWdoIHRoZSBmaXJzdCBxdWFydGVyIG9mIHRoZSB5ZWFyLCByZWFjaGluZyBhIGxvdyBhcm91bmQgQXByaWwgMjAyMy4gRnJvbSB0aGF0IHBvaW50IG9ud2FyZCwgdGhlIHBvcHVsYXJpdHkgcmVtYWlucyByZWxhdGl2ZWx5IHN0YWJsZSwgZmx1Y3R1YXRpbmcgYmV0d2VlbiA1LDAwMCBhbmQgNywwMDAsIHdpdGggb25seSBtaW5vciB1cHMgYW5kIGRvd25zLg0KDQpNaW5vciBGbHVjdHVhdGlvbnM6DQpUaHJvdWdob3V0IHRoZSByZXN0IG9mIHRoZSB5ZWFyLCBJIG5vdGljZWQgc29tZSBzbWFsbGVyIG9zY2lsbGF0aW9ucywgYnV0IG5vbmUgYXJlIGFzIGRyYW1hdGljIGFzIHRoZSBpbml0aWFsIGRyb3AuIFRoaXMgaW5kaWNhdGVzIHRoYXQgd2hpbGUgdGhlcmUgbWF5IGJlIHNvbWUgbWlub3Igc2Vhc29uYWwgZWZmZWN0cywgdGhlIG1haW4gc2Vhc29uYWxpdHkgaXMgY29uY2VudHJhdGVkIGF0IHRoZSBzdGFydCBvZiB0aGUgeWVhci4NCg0KT3V0bGllcnM6DQpUaGVyZSBhcmUgYSBmZXcgb3V0bGllciBwb2ludHMsIGVzcGVjaWFsbHkgaGlnaCB2YWx1ZXMsIHRoYXQgdGhlIHNtb290aGVyIGRvZXNu4oCZdCBmb2xsb3cgY2xvc2VseS4gVGhpcyBpcyBleHBlY3RlZCwgYXMgdGhlIHNtb290aGluZyBtZXRob2QgaXMgZGVzaWduZWQgdG8gY2FwdHVyZSB0aGUgb3ZlcmFsbCB0cmVuZCByYXRoZXIgdGhhbiBldmVyeSBpbmRpdmlkdWFsIGZsdWN0dWF0aW9uLg0KDQojIyMgQ29uY2x1c2lvbjoNCg0KRnJvbSB0aGlzIGFuYWx5c2lzLCBJIGNhbiBjb25jbHVkZSB0aGF0IHRoZXJlIGlzIGF0IGxlYXN0IG9uZSBzdHJvbmcgc2Vhc29uIGluIG15IGRhdGEsIHdpdGggYSBtYWpvciBwZWFrIGluIHBvcHVsYXJpdHkgYXQgdGhlIHN0YXJ0IG9mIHRoZSB5ZWFyLCBmb2xsb3dlZCBieSBhIHNoYXJwIGRlY2xpbmUgYW5kIGEgc3RhYmxlIHBlcmlvZC4gVGhpcyBzdWdnZXN0cyB0aGF0IHRpbWluZyBwbGF5cyBhIHNpZ25pZmljYW50IHJvbGUgaW4gcG9wdWxhcml0eSwgYW5kIGl0IG1heSBiZSBiZW5lZmljaWFsIHRvIGFsaWduIG1ham9yIHJlbGVhc2VzIG9yIHByb21vdGlvbnMgd2l0aCB0aGUgcGVyaW9kIHdoZXJlIHRoZSBwZWFrIG9jY3Vycy4NCg0KSWYgSSB3YW50IHRvIHVuZGVyc3RhbmQgdGhlIGNhdXNlcyBiZWhpbmQgdGhlc2UgdHJlbmRzIG1vcmUgZGVlcGx5LCBJIGNvdWxkIGZ1cnRoZXIgaW52ZXN0aWdhdGUgd2hhdCBoYXBwZW5lZCBkdXJpbmcgdGhlIHBlYWsgcGVyaW9kIG9yIHVzZSBtb3JlIGFkdmFuY2VkIHRpbWUgc2VyaWVzIGFuYWx5c2lzIHRlY2huaXF1ZXMuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==