Analyzing & Visualizing Spotify Data in RStudio

Authors: Drew An-Pham

Drew’s Portfolio


# set directory
knitr::opts_knit$set(root.dir = '~/Desktop/GDS_Code/') 
getwd()
[1] "/Users/drewderyk/Desktop/GDS_Code"
# install and add packages
packages = c("spotifyr", "knitr", "dplyr", "tidyverse", "lubridate", "ggplot2", "ggridges", "ggthemes", "ggrepel", "showtext", "plotly", "ggtern")
setdiff(packages, rownames(installed.packages()))
character(0)
install.packages(setdiff(packages, rownames(installed.packages())), quietly=TRUE)

library(spotifyr)
library(knitr)
library(dplyr)
library(tidyverse)
library(lubridate)
library(ggplot2)
library(ggridges)
library(ggthemes)
library(ggrepel)
library(showtext)
library(plotly)
library(ggtern)
font_add_google("Open Sans")
showtext_auto()

id <- '84607ecc540e480fb44eb4db772f48ec'
secret <- '8fad4cf309be4b2bbb546bf9daab6c3f'

Sys.setenv(SPOTIFY_CLIENT_ID = id)
Sys.setenv(SPOTIFY_CLIENT_SECRET = secret)
access_token <- get_spotify_access_token()

# how to find spotify playlist ID: https://clients.caster.fm/knowledgebase/110/How-to-find-Spotify-playlist-ID.html

# access 2017 spotify wrapped tracks
tracks_2017 <- get_playlist_tracks('37i9dQZF1E9TUK1PmO7gjS')
features_2017 <- get_track_audio_features(tracks_2017$track.id)
tracks_features_2017 <- tracks_2017 %>%
  left_join(features_2017, by = c('track.id' = 'id')) %>% # tracks field = features field
  mutate(song_order = 1:n()) 
head(tracks_features_2017)

# access 2020 spotify wrapped tracks
tracks_2020 <- get_playlist_tracks('37i9dQZF1EMcGvfb7im4AM')
features_2020 <- get_track_audio_features(tracks_2020$track.id)
tracks_features_2020 <- tracks_2020 %>%
  left_join(features_2020, by = c('track.id' = 'id')) %>% # tracks field = features field
  mutate(song_order = 1:n())
head(tracks_features_2020)

# create 2017 spotify wrapped bubble plot and export
wrapped_2017_graph <- ggplot(tracks_features_2017, aes(valence, danceability, label = track.name)) +
  # label makes a 3rd aesthetic that isn't visible in the static layout, but in the interactive
  geom_point(alpha = .7, aes(size = track.popularity, color = song_order)) + 
  labs(size = "Track Popularity") + 
  scale_size(range = c(0,6)) + # change range for graphic export to illustrator, works for md  
  scale_color_gradientn("My Top Songs", trans = "reverse", colors = c("seashell2", "lightgoldenrod1", "yellowgreen", "forestgreen")) +
  # alternative color palette: "lightsteelblue4", "lavender", "thistle3", "plum4"
  # geom_text_repel(aes(label = track.name), size = 1.5) + # label songs for reference on static graph
  theme_minimal() + 
  # ggtitle("The Moods & Popularity of \n My Spotify Wrapped 2017") + 
  # labs(x = "Valence (Musical Positiveness)", y = "Danceability") + 
  # theme(plot.title = element_text(hjust = 0.5),
  #       text = element_text(family = "Open Sans", size = 10))
  theme(panel.grid = element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank())

ggsave("Wrapped_2017.png",
       width = 17,
       height = 11,
       units = c("in"),
       bg='transparent')

# create 2020 spotify wrapped bubble plot and export
wrapped_2020_graph <- ggplot(tracks_features_2020, aes(valence, danceability, label = track.name)) +
  # label makes a 3rd aesthetic that isn't visible in the static layout, but in the interactive
  geom_point(alpha = .7, aes(size = track.popularity, color = song_order)) + 
  labs(size = "Track Popularity") + 
  scale_size(range = c(-2,6)) + # change range for graphic export to illustrator, works for md
  scale_color_gradientn("My Top Songs", trans = "reverse", colors = c("seashell2", "lightgoldenrod1", "yellowgreen", "forestgreen")) + 
  # geom_text_repel(aes(label = track.name), size = 1.5) + # label songs for reference on static graph
  theme_minimal() + 
  # ggtitle("The Moods & Popularity of \n My Spotify Wrapped 2020") + 
  # labs(x = "Valence (Musical Positiveness)", y = "Danceability") + 
  # theme(plot.title = element_text(hjust = 0.5),
  #       text = element_text(family = "Open Sans", size = 10))
  theme(panel.grid = element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank())
  
ggsave("Wrapped_2020.png",
       width = 17,
       height = 11,
       units = c("in"),
       bg='transparent')

wrapped_2017_graph

wrapped_2020_graph


# make static graphs interactive 
wrapped_2017_interactive <- ggplotly(wrapped_2017_graph)
wrapped_2020_interactive <- ggplotly(wrapped_2020_graph)
wrapped_2017_interactive
wrapped_2020_interactive
popularity_2017 <- mean(tracks_features_2017$track.popularity)
valence_2017 <- mean(tracks_features_2017$valence)
danceability_2017 <- mean(tracks_features_2017$danceability)

popularity_2020 <- mean(tracks_features_2020$track.popularity)
valence_2020 <- mean(tracks_features_2020$valence)
danceability_2020 <- mean(tracks_features_2020$danceability)

popularity_2017
[1] 39.95
valence_2017
[1] 0.411865
danceability_2017
[1] 0.6109
popularity_2020
[1] 45.27
valence_2020
[1] 0.470415
danceability_2020 
[1] 0.63608

The dot plots compare my Spotify listening habits from 2017 versus 2020. I decided to use my Spotify Wrapped playlists as a metric to quantify ‘listening habits,’ as these playlists compile the top 100 songs I listened to in a given year/music cycle. 2021 would have been used instead of 2020 for this analysis, allowing the comparison to be my earliest Wrapped vs most recent Wrapped, but the function get_playlist_tracks() didn’t work for 2021… probably because the generated playlist ID is fairly recent?

For my analysis, I focused on the mood on the songs I listened to and their relative popularity (objectively, by streams since release and subjectively, by personal listening amount). The dot plots then juggle 4-variables, 2 looking at mood and 2 looking at relative popularity. The intent of utilizing these variables was to

  • see if the mood of the songs I listened to correlated with my general lived experiences in a given year
  • see how ‘unique’ my music taste was in high school compared to university

Valence and danceability definitions were sourced from Spotify’s Audio Feature Reference page. Track Popularity defined by Spotify’s Web API page

NOTE: The metric track popularity is constantly changing for each individual song, yet the mean can gradually increase of decrease over time. For this analysis, average track popularity was taken from the date June 10th, 2022.

In 2017, I was experiencing the latter portion of my sophomore and the first half of my junior year. This period of my life was defined by having quit club volleyball at the beginning of year from a toxic team culture, lots of studying for my ACT/SAT, working at the Mueseum of Natural Sciences 3 days a week, and taking way too many AP courses that I never ended up getting credit for at university. My average track popularity in 2017 was 39.28, meaning I listened to a fair portion of understreamed/non-mainstream songs in 2017. Call me a Spotify listening pioneer. This lower track popularity score can be possibly related to the fact I found a lot of my favorite songs that year via Discover Weekly—a tailored playlist that recommends new songs for users weekly—and Fresh Finds—a curated playlist based on your listening habits that recommends newly released music from artists. This metric is interesting because a good portion of my ‘rarest’ songs in 2017 now have over 100 million streams on Spotify, meaning I began listening to these songs before they blew up. These include “Green Light” by Lorde, “Electric” Alina Baraz ft. Khalid, and “Sorry Not Sorry” by Demi Levato. On the other hand, there are songs with a low track popularity that are still understreamed, such as “In Your Corner” by Ella Vos and “Found You” by Kasbo ft. Chelsea Cutler. Beyond relative popularity, an interesting trend observed is that the mean danceability and valence for my Spotify 2017 Wrapped were lower than the same variables for my Spotify 2020 Wrapped. A 6% lower valence and 2% lower danceability may not seem significant, but these deviations suggest a slightly higher degree of overall happiness in 2020 than in 2017.

In 2020, whilst this was first lockdown during the pandemic, a bummer summer turned into blissful Fall at Middlebury may explain this deviation. Even though many students like myself were devastated about the impacts COVID would have on our college experience, at Middlebury, I was very fortunate to have majority in-person classes masked and was still able to partake in activities such as rowing. During this period as well, I started exercising a lot more to pass the time, so many of the songs identified with high valence/danceability were songs that got me through an intense run/row, such as “Don’t Start Now” by Dua Lipa and “Alone (Calvin Harris Remix) ft. Stefflon Don” by Halsey, Calvin Harris, and Stefflon Don. During 2020, however, my music taste started to take a generic turn, with my track popularity averaging 44.34, an almost 5 unit increase from 2017. By no means does this speak lowly on my music taste, however, as this was the year many of my peers on campus introduced me to great tracks and banger songs I wouldn’t have discovered myself. As well, our college’s music radio station was another avenue I found new music through, in junction with my Spotify Wrapped. So whilst the data shows a more generic turn in my track popularity, I would argue my music taste become more versatile genre wise, as more variable tracks made it on my Top 100 in 2020: some favorites include “Kiss of Life” by Sade, a 1992 jazz/synthpop song about enduring love, and “Same Space” by Tiana Major9, a 2020 R&B/Soul ballad looking about navigating relationships in the modern era.


# access every paramore track and their audio features
paramore <- get_artist_audio_features("Paramore")

# remove albums that are either deluxe or live recorded
paramore <- paramore[paramore$album_name != "The Final Riot!" & paramore$album_name != "Paramore (Deluxe Edition)" & paramore$album_name != "Brand New Eyes (Deluxe Edition)",]

# prepare data for ternary plot, create album groups (first, middle, last)
paramore_ternary <- paramore %>%
  mutate(group = factor(case_when(album_name == "After Laughter" ~ 1,
                                  album_name == "All We Know Is Falling" ~ 2,
                                  album_name == "Paramore" ~ 3,
                                  album_name == "Brand New Eyes" ~ 3,
                                  album_name == "Riot!" ~ 3,
                                  ))) 
paramore_ternary$group <- as.character(paramore_ternary$group)
# This next code chunk may cause a 'DataTables warning' to pop up. Just press ok 4 until the message disappears and proceed.
paramore_ternary$group[paramore_ternary$group == "1"] <- "After Laughter" 
paramore_ternary$group[paramore_ternary$group == "2"] <- "All We Know Is Falling"
paramore_ternary$group[paramore_ternary$group == "3"] <- "Other Albums"

# create and stylize ternary plot
paramore_ternary %>%
  ggplot(aes(x = valence,  y = energy, z = danceability, color = group, alpha = group)) +
  coord_tern() +
  labs(x = "Valence", y = "Energy", z = "Danceabilty") +
  geom_point(data = filter(paramore_ternary, group == "Other Albums"), size = 0.5) +
  geom_point(data = filter(paramore_ternary, group == "After Laughter"), size = 2) +
  geom_point(data = filter(paramore_ternary, group == "All We Know Is Falling"), size = 2) +
  scale_color_manual(values = c("indianred", "cornflowerblue", "seashell1")) +
  # alternative color palette: c("lightpink3", "lightskyblue3", "seashell1"))
  scale_alpha_manual(values = c(.75, .75, .1)) + 
  theme_noticks() +
  theme_hidegrid() + 
  theme(legend.title=element_blank(),
        legend.background = element_rect(fill = "black"),
        legend.key = element_rect(fill = "black", color = NA),
        legend.text = element_text(color = "ivory1")) +
  theme(plot.background = element_rect(fill = "black", color = "black"),
        panel.background = element_rect(fill = "black", color = "white"),
        axis.title = element_text(size = 10,
                                  color = "white",
                                  family = "Open Sans",
                                  hjust = .5))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
ggsave("paramore_ternary.jpg",
       width = 11.5,
       height = 8,
       units = c("in"))

al <- filter(paramore_ternary, album_name == "After Laughter")
awkif <- filter(paramore_ternary, album_name == "All We Know Is Falling")

valence_al <- mean(al$valence)
danceability_al <- mean(al$danceability)
energy_al <- mean(al$energy)

valence_awkif <- mean(awkif$valence)
danceability_awkif <- mean(awkif$danceability)
energy_awkif <- mean(awkif$energy)

valence_al
[1] 0.6719167
danceability_al
[1] 0.6671667
energy_al
[1] 0.7253333
valence_awkif
[1] 0.442
danceability_awkif
[1] 0.4709
energy_awkif
[1] 0.8716

One of my favorite bands of all time is Paramore. I first discovered their music after hearing Hayley William’s—the lead singer’s—vocals on B.o.B’s most listened to song: “Airplanes.” Ever since, I’ve been an avid listener of the band’s music throughout middle school all the way to university. My Spotify Wrapped in 2017 provides evidence of my everlasting fan behavior, with 4 of my top 100 listened songs that year being by them. After the announcement of the band’s hiatus in 2019, my music feed has slowly began to see less Paramore. However, in this analysis, I wanted to bring back my fervor for this band that’s been hibernating through a data graphic story. Between their first and last album, Paramore’s evolution as a group saw a transition from music that was Emo/Punk to the genres of New Wave/Synth Pop. Whilst the genre of Pop Rock has always existed for the band, I became curious if the genre shifts between Paramore’s first and last album could observed in the distinctions in mood audio features from each track of the respective albums.

In this analysis, the mood audio features I decided to investigate and plot were…

  • Valence: A measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks with high valence sound more positive (e.g. happy, cheerful, euphoric), while tracks with low valence sound more negative (e.g. sad, depressed, angry).

  • Danceability: Describes how suitable a track is for dancing based on a combination of musical elements including tempo, rhythm stability, beat strength, and overall regularity. A value of 0.0 is least danceable and 1.0 is most danceable.

  • Energy: A measure from 0.0 to 1.0 and represents a perceptual measure of intensity and activity. Typically, energetic tracks feel fast, loud, and noisy. For example, death metal has high energy, while a Bach prelude scores low on the scale. Perceptual features contributing to this attribute include dynamic range, perceived loudness, timbre, onset rate, and general entropy.

All definitions were sourced from Spotify’s Audio Feature Reference page.

Paramore’s 2005 elementary album, All We Know Is Falling, is a dance between Hayley William’s assertive vocals and the rock-stylings of drums and electric guitar. With an average energy of 0.8716, this album is filled with intense, loud, and timbre-filled music that became resonate of many punk/rock groups in the early 2000s. However, where energy was high, mean danceability and valence both scored below a 0.5, suggesting Paramore’s earliest album aimed to have you feel and resonate with darker subjects, not designed for the early 2000s floor. Ultimately, this album was made for you to listen, process, and feel, rather than blissfully move to. Evidence of this can be seen from an interview with Williams, where she remarks the main theme of All We Know is Falling pertained to the departure of one of their bassists and the divorce of her parents. Contrasting the opening album to the band’s music legacy, their concluding album After Laughter departs from their history of pop punk and emo, veering towards a bright, animated collection of songs. With both mean valence and danceability showing an over .2 unit increase from All We Know is Falling, scoring 0.6719 and 0.6672 respectively, After Laughter delivers a musically positive and danceable experience via its vibrance and upbeat records used to contrast its lyrical themes of masking misery and the anxiety of aging (hence, probably the reason for a more upper-middle valence). Albeit, while delivering a relatively high average energy of 0.7253, After Laughter fall 0.15 units short of their first album, suggesting a fall in perceived loudness and general entropy attributed to the band’s genre shift.

The ternary plot I created not only communicates these broader observations taken from my summary statistics, but also enable a deeper dive into each album at an individual song level. Contrasting the two albums at first glance, nearly all songs from All We Know is Falling are more in the direction of the Energy vertex, whereas with After Laughter, songs lean more towards the valence and danceability vertex’s. While it’s hard to difficult individudal points in R, in Illustrator, emphasis was drawn to each individual song from either After Laughter or All We Know is Falling that had the max/min valence, danceability, and energy. Here are those songs with a respective quote that drive their awarded moods:

  • Max Valence = “Rose-Colored Boy” from After Laughter
    • “Hey, man, we all can’t be like you. I wish we were all rose-colored too my rose-colored boy”
  • Min Valence = “Franklin” from All We Know is Falling
    • “It’s taking up our time again. Go back, we can’t go back at all”
  • Max Danceability = “Caught in the Middle” from After Laughter
    • “I’m just a little bit caught in the middle. I try to keep going but it’s not that simple”
  • Min Danceability = “Whoa” from All We Know is Falling
    • “And we got everybody singing Whoa, whoa-oh, whoa, whoa-oh”
  • Max Energy = “Emergency” from All We Know is Falling
    • “Cause I’ve seen love die way too many times when it deserved to be alive”
  • Min Energy = “26” from After Laughter
    • “Hold onto hope, if you got it. Don’t let it go for nobody”

# ANOVA - Is there a difference in the means of each mood audio features for each Paramore album?

# Create Box Plots
paramore_boxplot_valence <- ggplot(paramore, aes(x = album_name , y = valence)) + 
  geom_boxplot() +
  labs(title = "Album Name vs Valence",
       x = "Album Name", y = "Valence") +
  theme_bw()
# paramore_boxplot_valence # used to corroborate ANOVA result interpretations

paramore_boxplot_danceabilty <- ggplot(paramore, aes(x = album_name , y = danceability)) + 
  geom_boxplot() +
  labs(title = "Album Name vs Danceability",
       x = "Album Name", y = "Danceability") +
  theme_bw()
# paramore_boxplot_danceabilty # used to corroborate ANOVA result interpretations

paramore_boxplot_energy <- ggplot(paramore, aes(x = album_name , y = energy)) + 
  geom_boxplot() +
  labs(title = "Album Name vs Energy",
       x = "Album Name", y = "Energy") +
  theme_bw()
# paramore_boxplot_energy # used to corroborate ANOVA result interpretations

# Run ANOVA Tests
anova_valence <- lm(paramore$valence~paramore$album_name)
anova(anova_valence)

anova_danceability <- lm(paramore$danceability~paramore$album_name)
anova(anova_danceability)

anova_energy <- lm(paramore$energy~paramore$album_name)
anova(anova_energy)

Since the Pr(>F) is 0.01821 < 0.05, we are 95% confident there is a significant difference in valence across different Paramore albums; this is reflected in the box plot created. Since the Pr(>F) is 1.286e-08 < 0.05, we are 95% confident there is a significant difference in danceability across different Paramore albums; this is reflected in the box plot created. Since the Pr(>F) is 0.138 > 0.05, accept the null hypothesis, thus there is not a significant difference in energy across different Paramore albums; this is reflected in the box plot created.


For a future/upcoming personal project, inspired by Pablo Alvarez’s Red Hot Chili Peppers setlist visualization, I want to look at how an artist structures their concert for a newly released album in relation to the orginal order of their album. On June 15th, I’ll be attending Ravyn Lenae’s Hypnos Tour in London and jotting down her setlist at this concert. After, I’ll aim to create a visualization in R comparing the original order of Hypnos to the order of music presented at the concert. My aim will be to understand the temporal and mood differences between a recorded album and a live presentation of that album. The first piece of code below creates a data frame with each track from hypnos and its associated track audio features. I then added a column with each tracks times in seconds, which will aid in latter visualization.


# album link: https://open.spotify.com/album/5Y4hUd0FPvCed5lu7loMXZ?si=j0PilelXRDy97u4qSe23og

# access hypnos tracks and features
hypnos_tracks <- get_album_tracks("5Y4hUd0FPvCed5lu7loMXZ") 
hypnos_features <- get_track_audio_features(hypnos_tracks$id)
hypnos <- hypnos_tracks %>%
  left_join(hypnos_features, by = c('id' = 'id')) # tracks field = features field 
head(hypnos)

# create a list of track duration values (in seconds)
hypnos_track_duration <- c(84, 209, 251, 207, 227, 224, 192, 95, 189, 265, 239, 228, 112, 178, 263, 243)

# add track time duration (in seconds) column 
hypnos <- hypnos %>%
  mutate(hypnos_track_duration)

R-Studio Short Cuts to insert chunk: Cmd+Option+I to preview markdown: Cmd+Shift+K to run code: Cmd+Shift+Enter

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBBbmFseXppbmcgJiBWaXN1YWxpemluZyBTcG90aWZ5IERhdGEgaW4gUlN0dWRpbwoKIyMjIEF1dGhvcnM6IERyZXcgQW4tUGhhbSAKCiMjIyBbRHJldydzIFBvcnRmb2xpb10oaHR0cHM6Ly9kYXB0eC5teXBvcnRmb2xpby5jb20vKQoKYGBge3Igc2V0IGRpcmVjdG9yeSBhbmQgaW5zdGFsbCBsaWJyYXJpZXMsIGlubGNsdWRlID0gRiwgd2FybmluZyA9IEZ9CgojIHNldCBkaXJlY3RvcnkKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnfi9EZXNrdG9wL0dEU19Db2RlLycpIApnZXR3ZCgpCgojIGluc3RhbGwgYW5kIGFkZCBwYWNrYWdlcwpwYWNrYWdlcyA9IGMoInNwb3RpZnlyIiwgImtuaXRyIiwgImRwbHlyIiwgInRpZHl2ZXJzZSIsICJsdWJyaWRhdGUiLCAiZ2dwbG90MiIsICJnZ3JpZGdlcyIsICJnZ3RoZW1lcyIsICJnZ3JlcGVsIiwgInNob3d0ZXh0IiwgInBsb3RseSIsICJnZ3Rlcm4iKQpzZXRkaWZmKHBhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpCmluc3RhbGwucGFja2FnZXMoc2V0ZGlmZihwYWNrYWdlcywgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSwgcXVpZXRseT1UUlVFKQoKbGlicmFyeShzcG90aWZ5cikKbGlicmFyeShrbml0cikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzaG93dGV4dCkKbGlicmFyeShwbG90bHkpCmxpYnJhcnkoZ2d0ZXJuKQpgYGAKCmBgYHtyIHN0eWxlIGd1aWRlfQpmb250X2FkZF9nb29nbGUoIk9wZW4gU2FucyIpCnNob3d0ZXh0X2F1dG8oKQpgYGAKCmBgYHtyIHNldHRpbmcgaW5mb3JtYXRpb24gdG8gYWNjZXNzIEFQSX0KCmlkIDwtICc4NDYwN2VjYzU0MGU0ODBmYjQ0ZWI0ZGI3NzJmNDhlYycKc2VjcmV0IDwtICc4ZmFkNGNmMzA5YmU0YjJiYmI1NDZiZjlkYWFiNmMzZicKClN5cy5zZXRlbnYoU1BPVElGWV9DTElFTlRfSUQgPSBpZCkKU3lzLnNldGVudihTUE9USUZZX0NMSUVOVF9TRUNSRVQgPSBzZWNyZXQpCmFjY2Vzc190b2tlbiA8LSBnZXRfc3BvdGlmeV9hY2Nlc3NfdG9rZW4oKQpgYGAKCmBgYHtyIGFuYWx5emUgU3BvdGlmeSBXcmFwcGVkIFBsYXlsaXN0cyB1c2luZyBTcG90aWZ5Uiwgd2FybmluZyA9IEZ9CgojIGhvdyB0byBmaW5kIHNwb3RpZnkgcGxheWxpc3QgSUQ6IGh0dHBzOi8vY2xpZW50cy5jYXN0ZXIuZm0va25vd2xlZGdlYmFzZS8xMTAvSG93LXRvLWZpbmQtU3BvdGlmeS1wbGF5bGlzdC1JRC5odG1sCgojIGFjY2VzcyAyMDE3IHNwb3RpZnkgd3JhcHBlZCB0cmFja3MKdHJhY2tzXzIwMTcgPC0gZ2V0X3BsYXlsaXN0X3RyYWNrcygnMzdpOWRRWkYxRTlUVUsxUG1PN2dqUycpCmZlYXR1cmVzXzIwMTcgPC0gZ2V0X3RyYWNrX2F1ZGlvX2ZlYXR1cmVzKHRyYWNrc18yMDE3JHRyYWNrLmlkKQp0cmFja3NfZmVhdHVyZXNfMjAxNyA8LSB0cmFja3NfMjAxNyAlPiUKICBsZWZ0X2pvaW4oZmVhdHVyZXNfMjAxNywgYnkgPSBjKCd0cmFjay5pZCcgPSAnaWQnKSkgJT4lICMgdHJhY2tzIGZpZWxkID0gZmVhdHVyZXMgZmllbGQKICBtdXRhdGUoc29uZ19vcmRlciA9IDE6bigpKSAKaGVhZCh0cmFja3NfZmVhdHVyZXNfMjAxNykKCiMgYWNjZXNzIDIwMjAgc3BvdGlmeSB3cmFwcGVkIHRyYWNrcwp0cmFja3NfMjAyMCA8LSBnZXRfcGxheWxpc3RfdHJhY2tzKCczN2k5ZFFaRjFFTWNHdmZiN2ltNEFNJykKZmVhdHVyZXNfMjAyMCA8LSBnZXRfdHJhY2tfYXVkaW9fZmVhdHVyZXModHJhY2tzXzIwMjAkdHJhY2suaWQpCnRyYWNrc19mZWF0dXJlc18yMDIwIDwtIHRyYWNrc18yMDIwICU+JQogIGxlZnRfam9pbihmZWF0dXJlc18yMDIwLCBieSA9IGMoJ3RyYWNrLmlkJyA9ICdpZCcpKSAlPiUgIyB0cmFja3MgZmllbGQgPSBmZWF0dXJlcyBmaWVsZAogIG11dGF0ZShzb25nX29yZGVyID0gMTpuKCkpCmhlYWQodHJhY2tzX2ZlYXR1cmVzXzIwMjApCgojIGNyZWF0ZSAyMDE3IHNwb3RpZnkgd3JhcHBlZCBidWJibGUgcGxvdCBhbmQgZXhwb3J0CndyYXBwZWRfMjAxN19ncmFwaCA8LSBnZ3Bsb3QodHJhY2tzX2ZlYXR1cmVzXzIwMTcsIGFlcyh2YWxlbmNlLCBkYW5jZWFiaWxpdHksIGxhYmVsID0gdHJhY2submFtZSkpICsKICAjIGxhYmVsIG1ha2VzIGEgM3JkIGFlc3RoZXRpYyB0aGF0IGlzbid0IHZpc2libGUgaW4gdGhlIHN0YXRpYyBsYXlvdXQsIGJ1dCBpbiB0aGUgaW50ZXJhY3RpdmUKICBnZW9tX3BvaW50KGFscGhhID0gLjcsIGFlcyhzaXplID0gdHJhY2sucG9wdWxhcml0eSwgY29sb3IgPSBzb25nX29yZGVyKSkgKyAKICBsYWJzKHNpemUgPSAiVHJhY2sgUG9wdWxhcml0eSIpICsgCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMCw2KSkgKyAjIGNoYW5nZSByYW5nZSBmb3IgZ3JhcGhpYyBleHBvcnQgdG8gaWxsdXN0cmF0b3IsIHdvcmtzIGZvciBtZCAgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKCJNeSBUb3AgU29uZ3MiLCB0cmFucyA9ICJyZXZlcnNlIiwgY29sb3JzID0gYygic2Vhc2hlbGwyIiwgImxpZ2h0Z29sZGVucm9kMSIsICJ5ZWxsb3dncmVlbiIsICJmb3Jlc3RncmVlbiIpKSArCiAgIyBhbHRlcm5hdGl2ZSBjb2xvciBwYWxldHRlOiAibGlnaHRzdGVlbGJsdWU0IiwgImxhdmVuZGVyIiwgInRoaXN0bGUzIiwgInBsdW00IgogICMgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHRyYWNrLm5hbWUpLCBzaXplID0gMS41KSArICMgbGFiZWwgc29uZ3MgZm9yIHJlZmVyZW5jZSBvbiBzdGF0aWMgZ3JhcGgKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdndGl0bGUoIlRoZSBNb29kcyAmIFBvcHVsYXJpdHkgb2YgXG4gTXkgU3BvdGlmeSBXcmFwcGVkIDIwMTciKSArIAogICMgbGFicyh4ID0gIlZhbGVuY2UgKE11c2ljYWwgUG9zaXRpdmVuZXNzKSIsIHkgPSAiRGFuY2VhYmlsaXR5IikgKyAKICAjIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICMgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiT3BlbiBTYW5zIiwgc2l6ZSA9IDEwKSkKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSkKCmdnc2F2ZSgiV3JhcHBlZF8yMDE3LnBuZyIsCiAgICAgICB3aWR0aCA9IDE3LAogICAgICAgaGVpZ2h0ID0gMTEsCiAgICAgICB1bml0cyA9IGMoImluIiksCiAgICAgICBiZz0ndHJhbnNwYXJlbnQnKQoKIyBjcmVhdGUgMjAyMCBzcG90aWZ5IHdyYXBwZWQgYnViYmxlIHBsb3QgYW5kIGV4cG9ydAp3cmFwcGVkXzIwMjBfZ3JhcGggPC0gZ2dwbG90KHRyYWNrc19mZWF0dXJlc18yMDIwLCBhZXModmFsZW5jZSwgZGFuY2VhYmlsaXR5LCBsYWJlbCA9IHRyYWNrLm5hbWUpKSArCiAgIyBsYWJlbCBtYWtlcyBhIDNyZCBhZXN0aGV0aWMgdGhhdCBpc24ndCB2aXNpYmxlIGluIHRoZSBzdGF0aWMgbGF5b3V0LCBidXQgaW4gdGhlIGludGVyYWN0aXZlCiAgZ2VvbV9wb2ludChhbHBoYSA9IC43LCBhZXMoc2l6ZSA9IHRyYWNrLnBvcHVsYXJpdHksIGNvbG9yID0gc29uZ19vcmRlcikpICsgCiAgbGFicyhzaXplID0gIlRyYWNrIFBvcHVsYXJpdHkiKSArIAogIHNjYWxlX3NpemUocmFuZ2UgPSBjKC0yLDYpKSArICMgY2hhbmdlIHJhbmdlIGZvciBncmFwaGljIGV4cG9ydCB0byBpbGx1c3RyYXRvciwgd29ya3MgZm9yIG1kCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKCJNeSBUb3AgU29uZ3MiLCB0cmFucyA9ICJyZXZlcnNlIiwgY29sb3JzID0gYygic2Vhc2hlbGwyIiwgImxpZ2h0Z29sZGVucm9kMSIsICJ5ZWxsb3dncmVlbiIsICJmb3Jlc3RncmVlbiIpKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHRyYWNrLm5hbWUpLCBzaXplID0gMS41KSArICMgbGFiZWwgc29uZ3MgZm9yIHJlZmVyZW5jZSBvbiBzdGF0aWMgZ3JhcGgKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdndGl0bGUoIlRoZSBNb29kcyAmIFBvcHVsYXJpdHkgb2YgXG4gTXkgU3BvdGlmeSBXcmFwcGVkIDIwMjAiKSArIAogICMgbGFicyh4ID0gIlZhbGVuY2UgKE11c2ljYWwgUG9zaXRpdmVuZXNzKSIsIHkgPSAiRGFuY2VhYmlsaXR5IikgKyAKICAjIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICMgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiT3BlbiBTYW5zIiwgc2l6ZSA9IDEwKSkKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSkKICAKZ2dzYXZlKCJXcmFwcGVkXzIwMjAucG5nIiwKICAgICAgIHdpZHRoID0gMTcsCiAgICAgICBoZWlnaHQgPSAxMSwKICAgICAgIHVuaXRzID0gYygiaW4iKSwKICAgICAgIGJnPSd0cmFuc3BhcmVudCcpCgp3cmFwcGVkXzIwMTdfZ3JhcGgKd3JhcHBlZF8yMDIwX2dyYXBoCgojIG1ha2Ugc3RhdGljIGdyYXBocyBpbnRlcmFjdGl2ZSAKd3JhcHBlZF8yMDE3X2ludGVyYWN0aXZlIDwtIGdncGxvdGx5KHdyYXBwZWRfMjAxN19ncmFwaCkKd3JhcHBlZF8yMDIwX2ludGVyYWN0aXZlIDwtIGdncGxvdGx5KHdyYXBwZWRfMjAyMF9ncmFwaCkKd3JhcHBlZF8yMDE3X2ludGVyYWN0aXZlCndyYXBwZWRfMjAyMF9pbnRlcmFjdGl2ZQpgYGAKCmBgYCB7ciBkZXJpdmUgU3BvdGlmeSBBdWRpbyBGZWF0dXJlcyBTdW1tYXJ5IFN0YXRpc3RpY3MgZm9yIEFuYWx5c2lzLCB3YXJuaW5nID0gRn0KcG9wdWxhcml0eV8yMDE3IDwtIG1lYW4odHJhY2tzX2ZlYXR1cmVzXzIwMTckdHJhY2sucG9wdWxhcml0eSkKdmFsZW5jZV8yMDE3IDwtIG1lYW4odHJhY2tzX2ZlYXR1cmVzXzIwMTckdmFsZW5jZSkKZGFuY2VhYmlsaXR5XzIwMTcgPC0gbWVhbih0cmFja3NfZmVhdHVyZXNfMjAxNyRkYW5jZWFiaWxpdHkpCgpwb3B1bGFyaXR5XzIwMjAgPC0gbWVhbih0cmFja3NfZmVhdHVyZXNfMjAyMCR0cmFjay5wb3B1bGFyaXR5KQp2YWxlbmNlXzIwMjAgPC0gbWVhbih0cmFja3NfZmVhdHVyZXNfMjAyMCR2YWxlbmNlKQpkYW5jZWFiaWxpdHlfMjAyMCA8LSBtZWFuKHRyYWNrc19mZWF0dXJlc18yMDIwJGRhbmNlYWJpbGl0eSkKCnBvcHVsYXJpdHlfMjAxNwp2YWxlbmNlXzIwMTcKZGFuY2VhYmlsaXR5XzIwMTcKcG9wdWxhcml0eV8yMDIwCnZhbGVuY2VfMjAyMApkYW5jZWFiaWxpdHlfMjAyMCAKYGBgCgpUaGUgZG90IHBsb3RzIGNvbXBhcmUgbXkgU3BvdGlmeSBsaXN0ZW5pbmcgaGFiaXRzIGZyb20gMjAxNyB2ZXJzdXMgMjAyMC4gSSBkZWNpZGVkIHRvIHVzZSBteSBTcG90aWZ5IFdyYXBwZWQgcGxheWxpc3RzIGFzIGEgbWV0cmljIHRvIHF1YW50aWZ5ICdsaXN0ZW5pbmcgaGFiaXRzLCcgYXMgdGhlc2UgcGxheWxpc3RzIGNvbXBpbGUgdGhlIHRvcCAxMDAgc29uZ3MgSSBsaXN0ZW5lZCB0byBpbiBhIGdpdmVuIHllYXIvbXVzaWMgY3ljbGUuIDIwMjEgd291bGQgaGF2ZSBiZWVuIHVzZWQgaW5zdGVhZCBvZiAyMDIwIGZvciB0aGlzIGFuYWx5c2lzLCBhbGxvd2luZyB0aGUgY29tcGFyaXNvbiB0byBiZSBteSBlYXJsaWVzdCBXcmFwcGVkIHZzIG1vc3QgcmVjZW50IFdyYXBwZWQsIGJ1dCB0aGUgZnVuY3Rpb24gZ2V0X3BsYXlsaXN0X3RyYWNrcygpIGRpZG4ndCB3b3JrIGZvciAyMDIxLi4uIHByb2JhYmx5IGJlY2F1c2UgdGhlIGdlbmVyYXRlZCBwbGF5bGlzdCBJRCBpcyBmYWlybHkgcmVjZW50PwoKRm9yIG15IGFuYWx5c2lzLCBJIGZvY3VzZWQgb24gdGhlIG1vb2Qgb24gdGhlIHNvbmdzIEkgbGlzdGVuZWQgdG8gYW5kIHRoZWlyIHJlbGF0aXZlIHBvcHVsYXJpdHkgKG9iamVjdGl2ZWx5LCBieSBzdHJlYW1zIHNpbmNlIHJlbGVhc2UgYW5kIHN1YmplY3RpdmVseSwgYnkgcGVyc29uYWwgbGlzdGVuaW5nIGFtb3VudCkuIFRoZSBkb3QgcGxvdHMgdGhlbiBqdWdnbGUgNC12YXJpYWJsZXMsIDIgbG9va2luZyBhdCBtb29kIGFuZCAyIGxvb2tpbmcgYXQgcmVsYXRpdmUgcG9wdWxhcml0eS4gVGhlIGludGVudCBvZiB1dGlsaXppbmcgdGhlc2UgdmFyaWFibGVzIHdhcyB0byAKCi0gc2VlIGlmIHRoZSBtb29kIG9mIHRoZSBzb25ncyBJIGxpc3RlbmVkIHRvIGNvcnJlbGF0ZWQgd2l0aCBteSBnZW5lcmFsIGxpdmVkIGV4cGVyaWVuY2VzIGluIGEgZ2l2ZW4geWVhciAKLSBzZWUgaG93ICd1bmlxdWUnIG15IG11c2ljIHRhc3RlIHdhcyBpbiBoaWdoIHNjaG9vbCBjb21wYXJlZCB0byB1bml2ZXJzaXR5CgoKVmFsZW5jZSBhbmQgZGFuY2VhYmlsaXR5IGRlZmluaXRpb25zIHdlcmUgc291cmNlZCBmcm9tIFtTcG90aWZ5J3MgQXVkaW8gRmVhdHVyZSBSZWZlcmVuY2UgcGFnZV0oaHR0cHM6Ly9kZXZlbG9wZXIuc3BvdGlmeS5jb20vZG9jdW1lbnRhdGlvbi93ZWItYXBpL3JlZmVyZW5jZS8jL29wZXJhdGlvbnMvZ2V0LXNldmVyYWwtYXVkaW8tZmVhdHVyZXMpLiBUcmFjayBQb3B1bGFyaXR5IGRlZmluZWQgYnkgW1Nwb3RpZnkncyBXZWIgQVBJIHBhZ2VdKGh0dHBzOi8vZGV2ZWxvcGVyLnNwb3RpZnkuY29tL2RvY3VtZW50YXRpb24vd2ViLWFwaS9yZWZlcmVuY2UvIy8pCgoqKk5PVEU6IFRoZSBtZXRyaWMgdHJhY2sgcG9wdWxhcml0eSBpcyBjb25zdGFudGx5IGNoYW5naW5nIGZvciBlYWNoIGluZGl2aWR1YWwgc29uZywgeWV0IHRoZSBtZWFuIGNhbiBncmFkdWFsbHkgaW5jcmVhc2Ugb2YgZGVjcmVhc2Ugb3ZlciB0aW1lLiBGb3IgdGhpcyBhbmFseXNpcywgYXZlcmFnZSB0cmFjayBwb3B1bGFyaXR5IHdhcyB0YWtlbiBmcm9tIHRoZSBkYXRlIEp1bmUgMTB0aCwgMjAyMi4qKgoKSW4gMjAxNywgSSB3YXMgZXhwZXJpZW5jaW5nIHRoZSBsYXR0ZXIgcG9ydGlvbiBvZiBteSBzb3Bob21vcmUgYW5kIHRoZSBmaXJzdCBoYWxmIG9mIG15IGp1bmlvciB5ZWFyLiBUaGlzIHBlcmlvZCBvZiBteSBsaWZlIHdhcyBkZWZpbmVkIGJ5IGhhdmluZyBxdWl0IGNsdWIgdm9sbGV5YmFsbCBhdCB0aGUgYmVnaW5uaW5nIG9mIHllYXIgZnJvbSBhIHRveGljIHRlYW0gY3VsdHVyZSwgbG90cyBvZiBzdHVkeWluZyBmb3IgbXkgQUNUL1NBVCwgd29ya2luZyBhdCB0aGUgTXVlc2V1bSBvZiBOYXR1cmFsIFNjaWVuY2VzIDMgZGF5cyBhIHdlZWssIGFuZCB0YWtpbmcgd2F5IHRvbyBtYW55IEFQIGNvdXJzZXMgdGhhdCBJIG5ldmVyIGVuZGVkIHVwIGdldHRpbmcgY3JlZGl0IGZvciBhdCB1bml2ZXJzaXR5LiBNeSBhdmVyYWdlIHRyYWNrIHBvcHVsYXJpdHkgaW4gMjAxNyB3YXMgMzkuMjgsIG1lYW5pbmcgSSBsaXN0ZW5lZCB0byBhIGZhaXIgcG9ydGlvbiBvZiB1bmRlcnN0cmVhbWVkL25vbi1tYWluc3RyZWFtIHNvbmdzIGluIDIwMTcuIENhbGwgbWUgYSBTcG90aWZ5IGxpc3RlbmluZyBwaW9uZWVyLiBUaGlzIGxvd2VyIHRyYWNrIHBvcHVsYXJpdHkgc2NvcmUgY2FuIGJlIHBvc3NpYmx5IHJlbGF0ZWQgdG8gdGhlIGZhY3QgSSBmb3VuZCBhIGxvdCBvZiBteSBmYXZvcml0ZSBzb25ncyB0aGF0IHllYXIgdmlhIERpc2NvdmVyIFdlZWtseeKAlGEgdGFpbG9yZWQgcGxheWxpc3QgdGhhdCByZWNvbW1lbmRzIG5ldyBzb25ncyBmb3IgdXNlcnMgd2Vla2x54oCUYW5kIEZyZXNoIEZpbmRz4oCUYSBjdXJhdGVkIHBsYXlsaXN0IGJhc2VkIG9uIHlvdXIgbGlzdGVuaW5nIGhhYml0cyB0aGF0IHJlY29tbWVuZHMgbmV3bHkgcmVsZWFzZWQgbXVzaWMgZnJvbSBhcnRpc3RzLiBUaGlzIG1ldHJpYyBpcyBpbnRlcmVzdGluZyBiZWNhdXNlIGEgZ29vZCBwb3J0aW9uIG9mIG15ICdyYXJlc3QnIHNvbmdzIGluIDIwMTcgbm93IGhhdmUgb3ZlciAxMDAgbWlsbGlvbiBzdHJlYW1zIG9uIFNwb3RpZnksIG1lYW5pbmcgSSBiZWdhbiBsaXN0ZW5pbmcgdG8gdGhlc2Ugc29uZ3MgYmVmb3JlIHRoZXkgYmxldyB1cC4gVGhlc2UgaW5jbHVkZSAiR3JlZW4gTGlnaHQiIGJ5IExvcmRlLCAiRWxlY3RyaWMiIEFsaW5hIEJhcmF6IGZ0LiBLaGFsaWQsIGFuZCAiU29ycnkgTm90IFNvcnJ5IiBieSBEZW1pIExldmF0by4gT24gdGhlIG90aGVyIGhhbmQsIHRoZXJlIGFyZSBzb25ncyB3aXRoIGEgbG93IHRyYWNrIHBvcHVsYXJpdHkgdGhhdCBhcmUgc3RpbGwgdW5kZXJzdHJlYW1lZCwgc3VjaCBhcyAiSW4gWW91ciBDb3JuZXIiIGJ5IEVsbGEgVm9zIGFuZCAiRm91bmQgWW91IiBieSBLYXNibyBmdC4gQ2hlbHNlYSBDdXRsZXIuIEJleW9uZCByZWxhdGl2ZSBwb3B1bGFyaXR5LCBhbiBpbnRlcmVzdGluZyB0cmVuZCBvYnNlcnZlZCBpcyB0aGF0IHRoZSBtZWFuIGRhbmNlYWJpbGl0eSBhbmQgdmFsZW5jZSBmb3IgbXkgU3BvdGlmeSAyMDE3IFdyYXBwZWQgd2VyZSBsb3dlciB0aGFuIHRoZSBzYW1lIHZhcmlhYmxlcyBmb3IgbXkgU3BvdGlmeSAyMDIwIFdyYXBwZWQuIEEgNiUgbG93ZXIgdmFsZW5jZSBhbmQgMiUgbG93ZXIgZGFuY2VhYmlsaXR5IG1heSBub3Qgc2VlbSBzaWduaWZpY2FudCwgYnV0IHRoZXNlIGRldmlhdGlvbnMgc3VnZ2VzdCBhIHNsaWdodGx5IGhpZ2hlciBkZWdyZWUgb2Ygb3ZlcmFsbCBoYXBwaW5lc3MgaW4gMjAyMCB0aGFuIGluIDIwMTcuIAoKSW4gMjAyMCwgd2hpbHN0IHRoaXMgd2FzIGZpcnN0IGxvY2tkb3duIGR1cmluZyB0aGUgcGFuZGVtaWMsIGEgYnVtbWVyIHN1bW1lciB0dXJuZWQgaW50byBibGlzc2Z1bCBGYWxsIGF0IE1pZGRsZWJ1cnkgbWF5IGV4cGxhaW4gdGhpcyBkZXZpYXRpb24uIEV2ZW4gdGhvdWdoIG1hbnkgc3R1ZGVudHMgbGlrZSBteXNlbGYgd2VyZSBkZXZhc3RhdGVkIGFib3V0IHRoZSBpbXBhY3RzIENPVklEIHdvdWxkIGhhdmUgb24gb3VyIGNvbGxlZ2UgZXhwZXJpZW5jZSwgYXQgTWlkZGxlYnVyeSwgSSB3YXMgdmVyeSBmb3J0dW5hdGUgdG8gaGF2ZSBtYWpvcml0eSBpbi1wZXJzb24gY2xhc3NlcyBtYXNrZWQgYW5kIHdhcyBzdGlsbCBhYmxlIHRvIHBhcnRha2UgaW4gYWN0aXZpdGllcyBzdWNoIGFzIHJvd2luZy4gRHVyaW5nIHRoaXMgcGVyaW9kIGFzIHdlbGwsIEkgc3RhcnRlZCBleGVyY2lzaW5nIGEgbG90IG1vcmUgdG8gcGFzcyB0aGUgdGltZSwgc28gbWFueSBvZiB0aGUgc29uZ3MgaWRlbnRpZmllZCB3aXRoIGhpZ2ggdmFsZW5jZS9kYW5jZWFiaWxpdHkgd2VyZSBzb25ncyB0aGF0IGdvdCBtZSB0aHJvdWdoIGFuIGludGVuc2UgcnVuL3Jvdywgc3VjaCBhcyAiRG9uJ3QgU3RhcnQgTm93IiBieSBEdWEgTGlwYSBhbmQgIkFsb25lIChDYWx2aW4gSGFycmlzIFJlbWl4KSBmdC4gU3RlZmZsb24gRG9uIiBieSBIYWxzZXksIENhbHZpbiBIYXJyaXMsIGFuZCBTdGVmZmxvbiBEb24uIER1cmluZyAyMDIwLCBob3dldmVyLCBteSBtdXNpYyB0YXN0ZSBzdGFydGVkIHRvIHRha2UgYSBnZW5lcmljIHR1cm4sIHdpdGggbXkgdHJhY2sgcG9wdWxhcml0eSBhdmVyYWdpbmcgNDQuMzQsIGFuIGFsbW9zdCA1IHVuaXQgaW5jcmVhc2UgZnJvbSAyMDE3LiBCeSBubyBtZWFucyBkb2VzIHRoaXMgc3BlYWsgbG93bHkgb24gbXkgbXVzaWMgdGFzdGUsIGhvd2V2ZXIsIGFzIHRoaXMgd2FzIHRoZSB5ZWFyIG1hbnkgb2YgbXkgcGVlcnMgb24gY2FtcHVzIGludHJvZHVjZWQgbWUgdG8gZ3JlYXQgdHJhY2tzIGFuZCBiYW5nZXIgc29uZ3MgSSB3b3VsZG4ndCBoYXZlIGRpc2NvdmVyZWQgbXlzZWxmLiBBcyB3ZWxsLCBvdXIgY29sbGVnZSdzIG11c2ljIHJhZGlvIHN0YXRpb24gd2FzIGFub3RoZXIgYXZlbnVlIEkgZm91bmQgbmV3IG11c2ljIHRocm91Z2gsIGluIGp1bmN0aW9uIHdpdGggbXkgU3BvdGlmeSBXcmFwcGVkLiBTbyB3aGlsc3QgdGhlIGRhdGEgc2hvd3MgYSBtb3JlIGdlbmVyaWMgdHVybiBpbiBteSB0cmFjayBwb3B1bGFyaXR5LCBJIHdvdWxkIGFyZ3VlIG15IG11c2ljIHRhc3RlIGJlY29tZSBtb3JlIHZlcnNhdGlsZSBnZW5yZSB3aXNlLCBhcyBtb3JlIHZhcmlhYmxlIHRyYWNrcyBtYWRlIGl0IG9uIG15IFRvcCAxMDAgaW4gMjAyMDogc29tZSBmYXZvcml0ZXMgaW5jbHVkZSAiS2lzcyBvZiBMaWZlIiBieSBTYWRlLCBhIDE5OTIgamF6ei9zeW50aHBvcCBzb25nIGFib3V0IGVuZHVyaW5nIGxvdmUsIGFuZCAiU2FtZSBTcGFjZSIgYnkgVGlhbmEgTWFqb3I5LCBhIDIwMjAgUiZCL1NvdWwgYmFsbGFkIGxvb2tpbmcgYWJvdXQgbmF2aWdhdGluZyByZWxhdGlvbnNoaXBzIGluIHRoZSBtb2Rlcm4gZXJhLiAgCgpgYGB7ciBQYXJhbW9yZSBUaGVuIFZlcnN1cyBOb3csIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQoKIyBhY2Nlc3MgZXZlcnkgcGFyYW1vcmUgdHJhY2sgYW5kIHRoZWlyIGF1ZGlvIGZlYXR1cmVzCnBhcmFtb3JlIDwtIGdldF9hcnRpc3RfYXVkaW9fZmVhdHVyZXMoIlBhcmFtb3JlIikKCiMgcmVtb3ZlIGFsYnVtcyB0aGF0IGFyZSBlaXRoZXIgZGVsdXhlIG9yIGxpdmUgcmVjb3JkZWQKcGFyYW1vcmUgPC0gcGFyYW1vcmVbcGFyYW1vcmUkYWxidW1fbmFtZSAhPSAiVGhlIEZpbmFsIFJpb3QhIiAmIHBhcmFtb3JlJGFsYnVtX25hbWUgIT0gIlBhcmFtb3JlIChEZWx1eGUgRWRpdGlvbikiICYgcGFyYW1vcmUkYWxidW1fbmFtZSAhPSAiQnJhbmQgTmV3IEV5ZXMgKERlbHV4ZSBFZGl0aW9uKSIsXQoKIyBwcmVwYXJlIGRhdGEgZm9yIHRlcm5hcnkgcGxvdCwgY3JlYXRlIGFsYnVtIGdyb3VwcyAoZmlyc3QsIG1pZGRsZSwgbGFzdCkKcGFyYW1vcmVfdGVybmFyeSA8LSBwYXJhbW9yZSAlPiUKICBtdXRhdGUoZ3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKGFsYnVtX25hbWUgPT0gIkFmdGVyIExhdWdodGVyIiB+IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGJ1bV9uYW1lID09ICJBbGwgV2UgS25vdyBJcyBGYWxsaW5nIiB+IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGJ1bV9uYW1lID09ICJQYXJhbW9yZSIgfiAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxidW1fbmFtZSA9PSAiQnJhbmQgTmV3IEV5ZXMiIH4gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsYnVtX25hbWUgPT0gIlJpb3QhIiB+IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkgCnBhcmFtb3JlX3Rlcm5hcnkkZ3JvdXAgPC0gYXMuY2hhcmFjdGVyKHBhcmFtb3JlX3Rlcm5hcnkkZ3JvdXApCiMgVGhpcyBuZXh0IGNvZGUgY2h1bmsgbWF5IGNhdXNlIGEgJ0RhdGFUYWJsZXMgd2FybmluZycgdG8gcG9wIHVwLiBKdXN0IHByZXNzIG9rIDQgdW50aWwgdGhlIG1lc3NhZ2UgZGlzYXBwZWFycyBhbmQgcHJvY2VlZC4KcGFyYW1vcmVfdGVybmFyeSRncm91cFtwYXJhbW9yZV90ZXJuYXJ5JGdyb3VwID09ICIxIl0gPC0gIkFmdGVyIExhdWdodGVyIiAKcGFyYW1vcmVfdGVybmFyeSRncm91cFtwYXJhbW9yZV90ZXJuYXJ5JGdyb3VwID09ICIyIl0gPC0gIkFsbCBXZSBLbm93IElzIEZhbGxpbmciCnBhcmFtb3JlX3Rlcm5hcnkkZ3JvdXBbcGFyYW1vcmVfdGVybmFyeSRncm91cCA9PSAiMyJdIDwtICJPdGhlciBBbGJ1bXMiCgojIGNyZWF0ZSBhbmQgc3R5bGl6ZSB0ZXJuYXJ5IHBsb3QKcGFyYW1vcmVfdGVybmFyeSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWxlbmNlLCAgeSA9IGVuZXJneSwgeiA9IGRhbmNlYWJpbGl0eSwgY29sb3IgPSBncm91cCwgYWxwaGEgPSBncm91cCkpICsKICBjb29yZF90ZXJuKCkgKwogIGxhYnMoeCA9ICJWYWxlbmNlIiwgeSA9ICJFbmVyZ3kiLCB6ID0gIkRhbmNlYWJpbHR5IikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbHRlcihwYXJhbW9yZV90ZXJuYXJ5LCBncm91cCA9PSAiT3RoZXIgQWxidW1zIiksIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXIocGFyYW1vcmVfdGVybmFyeSwgZ3JvdXAgPT0gIkFmdGVyIExhdWdodGVyIiksIHNpemUgPSAyKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZmlsdGVyKHBhcmFtb3JlX3Rlcm5hcnksIGdyb3VwID09ICJBbGwgV2UgS25vdyBJcyBGYWxsaW5nIiksIHNpemUgPSAyKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImluZGlhbnJlZCIsICJjb3JuZmxvd2VyYmx1ZSIsICJzZWFzaGVsbDEiKSkgKwogICMgYWx0ZXJuYXRpdmUgY29sb3IgcGFsZXR0ZTogYygibGlnaHRwaW5rMyIsICJsaWdodHNreWJsdWUzIiwgInNlYXNoZWxsMSIpKQogIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKC43NSwgLjc1LCAuMSkpICsgCiAgdGhlbWVfbm90aWNrcygpICsKICB0aGVtZV9oaWRlZ3JpZCgpICsgCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImJsYWNrIiksCiAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gImJsYWNrIiwgY29sb3IgPSBOQSksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiaXZvcnkxIikpICsKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJibGFjayIsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImJsYWNrIiwgY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJPcGVuIFNhbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAuNSkpCmdnc2F2ZSgicGFyYW1vcmVfdGVybmFyeS5qcGciLAogICAgICAgd2lkdGggPSAxMS41LAogICAgICAgaGVpZ2h0ID0gOCwKICAgICAgIHVuaXRzID0gYygiaW4iKSkKYGBgCgpgYGB7ciBkZXJpdmUgUGFyYW1vcmUgQWxidW0gU3VtbWFyeSBTdGF0aXN0aWNzIGZvciBBbmFseXNpc30KYWwgPC0gZmlsdGVyKHBhcmFtb3JlX3Rlcm5hcnksIGFsYnVtX25hbWUgPT0gIkFmdGVyIExhdWdodGVyIikKYXdraWYgPC0gZmlsdGVyKHBhcmFtb3JlX3Rlcm5hcnksIGFsYnVtX25hbWUgPT0gIkFsbCBXZSBLbm93IElzIEZhbGxpbmciKQoKdmFsZW5jZV9hbCA8LSBtZWFuKGFsJHZhbGVuY2UpCmRhbmNlYWJpbGl0eV9hbCA8LSBtZWFuKGFsJGRhbmNlYWJpbGl0eSkKZW5lcmd5X2FsIDwtIG1lYW4oYWwkZW5lcmd5KQoKdmFsZW5jZV9hd2tpZiA8LSBtZWFuKGF3a2lmJHZhbGVuY2UpCmRhbmNlYWJpbGl0eV9hd2tpZiA8LSBtZWFuKGF3a2lmJGRhbmNlYWJpbGl0eSkKZW5lcmd5X2F3a2lmIDwtIG1lYW4oYXdraWYkZW5lcmd5KQoKdmFsZW5jZV9hbApkYW5jZWFiaWxpdHlfYWwKZW5lcmd5X2FsCnZhbGVuY2VfYXdraWYKZGFuY2VhYmlsaXR5X2F3a2lmCmVuZXJneV9hd2tpZgpgYGAKCk9uZSBvZiBteSBmYXZvcml0ZSBiYW5kcyBvZiBhbGwgdGltZSBpcyBQYXJhbW9yZS4gSSBmaXJzdCBkaXNjb3ZlcmVkIHRoZWlyIG11c2ljIGFmdGVyIGhlYXJpbmcgSGF5bGV5IFdpbGxpYW0nc+KAlHRoZSBsZWFkIHNpbmdlcidz4oCUdm9jYWxzIG9uIEIuby5CJ3MgbW9zdCBsaXN0ZW5lZCB0byBzb25nOiAiQWlycGxhbmVzLiIgRXZlciBzaW5jZSwgSSd2ZSBiZWVuIGFuIGF2aWQgbGlzdGVuZXIgb2YgdGhlIGJhbmQncyBtdXNpYyB0aHJvdWdob3V0IG1pZGRsZSBzY2hvb2wgYWxsIHRoZSB3YXkgdG8gdW5pdmVyc2l0eS4gTXkgU3BvdGlmeSBXcmFwcGVkIGluIDIwMTcgcHJvdmlkZXMgZXZpZGVuY2Ugb2YgbXkgZXZlcmxhc3RpbmcgZmFuIGJlaGF2aW9yLCB3aXRoIDQgb2YgbXkgdG9wIDEwMCBsaXN0ZW5lZCBzb25ncyB0aGF0IHllYXIgYmVpbmcgYnkgdGhlbS4gQWZ0ZXIgdGhlIGFubm91bmNlbWVudCBvZiB0aGUgYmFuZCdzIGhpYXR1cyBpbiAyMDE5LCBteSBtdXNpYyBmZWVkIGhhcyBzbG93bHkgYmVnYW4gdG8gc2VlIGxlc3MgUGFyYW1vcmUuIEhvd2V2ZXIsIGluIHRoaXMgYW5hbHlzaXMsIEkgd2FudGVkIHRvIGJyaW5nIGJhY2sgbXkgZmVydm9yIGZvciB0aGlzIGJhbmQgdGhhdCdzIGJlZW4gaGliZXJuYXRpbmcgdGhyb3VnaCBhIGRhdGEgZ3JhcGhpYyBzdG9yeS4gQmV0d2VlbiB0aGVpciBmaXJzdCBhbmQgbGFzdCBhbGJ1bSwgUGFyYW1vcmUncyBldm9sdXRpb24gYXMgYSBncm91cCBzYXcgYSB0cmFuc2l0aW9uIGZyb20gbXVzaWMgdGhhdCB3YXMgRW1vL1B1bmsgdG8gdGhlIGdlbnJlcyBvZiBOZXcgV2F2ZS9TeW50aCBQb3AuIFdoaWxzdCB0aGUgZ2VucmUgb2YgUG9wIFJvY2sgaGFzIGFsd2F5cyBleGlzdGVkIGZvciB0aGUgYmFuZCwgSSBiZWNhbWUgY3VyaW91cyBpZiB0aGUgZ2VucmUgc2hpZnRzIGJldHdlZW4gUGFyYW1vcmUncyBmaXJzdCBhbmQgbGFzdCBhbGJ1bSBjb3VsZCBvYnNlcnZlZCBpbiB0aGUgZGlzdGluY3Rpb25zIGluIG1vb2QgYXVkaW8gZmVhdHVyZXMgZnJvbSBlYWNoIHRyYWNrIG9mIHRoZSByZXNwZWN0aXZlIGFsYnVtcy4KCkluIHRoaXMgYW5hbHlzaXMsIHRoZSBtb29kIGF1ZGlvIGZlYXR1cmVzIEkgZGVjaWRlZCB0byBpbnZlc3RpZ2F0ZSBhbmQgcGxvdCB3ZXJlLi4uCgorIFZhbGVuY2U6IEEgbWVhc3VyZSBmcm9tIDAuMCB0byAxLjAgZGVzY3JpYmluZyB0aGUgbXVzaWNhbCBwb3NpdGl2ZW5lc3MgY29udmV5ZWQgYnkgYSB0cmFjay4gVHJhY2tzIHdpdGggaGlnaCB2YWxlbmNlIHNvdW5kIG1vcmUgcG9zaXRpdmUgKGUuZy4gaGFwcHksIGNoZWVyZnVsLCBldXBob3JpYyksIHdoaWxlIHRyYWNrcyB3aXRoIGxvdyB2YWxlbmNlIHNvdW5kIG1vcmUgbmVnYXRpdmUgKGUuZy4gc2FkLCBkZXByZXNzZWQsIGFuZ3J5KS4KCisgRGFuY2VhYmlsaXR5OiBEZXNjcmliZXMgaG93IHN1aXRhYmxlIGEgdHJhY2sgaXMgZm9yIGRhbmNpbmcgYmFzZWQgb24gYSBjb21iaW5hdGlvbiBvZiBtdXNpY2FsIGVsZW1lbnRzIGluY2x1ZGluZyB0ZW1wbywgcmh5dGhtIHN0YWJpbGl0eSwgYmVhdCBzdHJlbmd0aCwgYW5kIG92ZXJhbGwgcmVndWxhcml0eS4gQSB2YWx1ZSBvZiAwLjAgaXMgbGVhc3QgZGFuY2VhYmxlIGFuZCAxLjAgaXMgbW9zdCBkYW5jZWFibGUuCgorIEVuZXJneTogQSBtZWFzdXJlIGZyb20gMC4wIHRvIDEuMCBhbmQgcmVwcmVzZW50cyBhIHBlcmNlcHR1YWwgbWVhc3VyZSBvZiBpbnRlbnNpdHkgYW5kIGFjdGl2aXR5LiBUeXBpY2FsbHksIGVuZXJnZXRpYyB0cmFja3MgZmVlbCBmYXN0LCBsb3VkLCBhbmQgbm9pc3kuIEZvciBleGFtcGxlLCBkZWF0aCBtZXRhbCBoYXMgaGlnaCBlbmVyZ3ksIHdoaWxlIGEgQmFjaCBwcmVsdWRlIHNjb3JlcyBsb3cgb24gdGhlIHNjYWxlLiBQZXJjZXB0dWFsIGZlYXR1cmVzIGNvbnRyaWJ1dGluZyB0byB0aGlzIGF0dHJpYnV0ZSBpbmNsdWRlIGR5bmFtaWMgcmFuZ2UsIHBlcmNlaXZlZCBsb3VkbmVzcywgdGltYnJlLCBvbnNldCByYXRlLCBhbmQgZ2VuZXJhbCBlbnRyb3B5LgoKQWxsIGRlZmluaXRpb25zIHdlcmUgc291cmNlZCBmcm9tIFtTcG90aWZ5J3MgQXVkaW8gRmVhdHVyZSBSZWZlcmVuY2UgcGFnZV0oaHR0cHM6Ly9kZXZlbG9wZXIuc3BvdGlmeS5jb20vZG9jdW1lbnRhdGlvbi93ZWItYXBpL3JlZmVyZW5jZS8jL29wZXJhdGlvbnMvZ2V0LXNldmVyYWwtYXVkaW8tZmVhdHVyZXMpLgoKUGFyYW1vcmUncyAyMDA1IGVsZW1lbnRhcnkgYWxidW0sICpBbGwgV2UgS25vdyBJcyBGYWxsaW5nKiwgaXMgYSBkYW5jZSBiZXR3ZWVuIEhheWxleSBXaWxsaWFtJ3MgYXNzZXJ0aXZlIHZvY2FscyBhbmQgdGhlIHJvY2stc3R5bGluZ3Mgb2YgZHJ1bXMgYW5kIGVsZWN0cmljIGd1aXRhci4gV2l0aCBhbiBhdmVyYWdlIGVuZXJneSBvZiAwLjg3MTYsIHRoaXMgYWxidW0gaXMgZmlsbGVkIHdpdGggaW50ZW5zZSwgbG91ZCwgYW5kIHRpbWJyZS1maWxsZWQgbXVzaWMgdGhhdCBiZWNhbWUgcmVzb25hdGUgb2YgbWFueSBwdW5rL3JvY2sgZ3JvdXBzIGluIHRoZSBlYXJseSAyMDAwcy4gSG93ZXZlciwgd2hlcmUgZW5lcmd5IHdhcyBoaWdoLCBtZWFuIGRhbmNlYWJpbGl0eSBhbmQgdmFsZW5jZSBib3RoIHNjb3JlZCBiZWxvdyBhIDAuNSwgc3VnZ2VzdGluZyBQYXJhbW9yZSdzIGVhcmxpZXN0IGFsYnVtIGFpbWVkIHRvIGhhdmUgeW91IGZlZWwgYW5kIHJlc29uYXRlIHdpdGggZGFya2VyIHN1YmplY3RzLCBub3QgZGVzaWduZWQgZm9yIHRoZSBlYXJseSAyMDAwcyBmbG9vci4gVWx0aW1hdGVseSwgdGhpcyBhbGJ1bSB3YXMgbWFkZSBmb3IgeW91IHRvIGxpc3RlbiwgcHJvY2VzcywgYW5kIGZlZWwsIHJhdGhlciB0aGFuIGJsaXNzZnVsbHkgbW92ZSB0by4gRXZpZGVuY2Ugb2YgdGhpcyBjYW4gYmUgc2VlbiBmcm9tIGFuIGludGVydmlldyB3aXRoIFdpbGxpYW1zLCB3aGVyZSBzaGUgcmVtYXJrcyB0aGUgbWFpbiB0aGVtZSBvZiAqQWxsIFdlIEtub3cgaXMgRmFsbGluZyogcGVydGFpbmVkIHRvIHRoZSBkZXBhcnR1cmUgb2Ygb25lIG9mIHRoZWlyIGJhc3Npc3RzIGFuZCB0aGUgZGl2b3JjZSBvZiBoZXIgcGFyZW50cy4gQ29udHJhc3RpbmcgdGhlIG9wZW5pbmcgYWxidW0gdG8gdGhlIGJhbmQncyBtdXNpYyBsZWdhY3ksIHRoZWlyIGNvbmNsdWRpbmcgYWxidW0gKkFmdGVyIExhdWdodGVyKiBkZXBhcnRzIGZyb20gdGhlaXIgaGlzdG9yeSBvZiBwb3AgcHVuayBhbmQgZW1vLCB2ZWVyaW5nIHRvd2FyZHMgYSBicmlnaHQsIGFuaW1hdGVkIGNvbGxlY3Rpb24gb2Ygc29uZ3MuIFdpdGggYm90aCBtZWFuIHZhbGVuY2UgYW5kIGRhbmNlYWJpbGl0eSBzaG93aW5nIGFuIG92ZXIgLjIgdW5pdCBpbmNyZWFzZSBmcm9tICpBbGwgV2UgS25vdyBpcyBGYWxsaW5nKiwgc2NvcmluZyAwLjY3MTkgYW5kIDAuNjY3MiByZXNwZWN0aXZlbHksICpBZnRlciBMYXVnaHRlciogZGVsaXZlcnMgYSBtdXNpY2FsbHkgcG9zaXRpdmUgYW5kIGRhbmNlYWJsZSBleHBlcmllbmNlIHZpYSBpdHMgdmlicmFuY2UgYW5kIHVwYmVhdCByZWNvcmRzIHVzZWQgdG8gY29udHJhc3QgaXRzIGx5cmljYWwgdGhlbWVzIG9mIG1hc2tpbmcgbWlzZXJ5IGFuZCB0aGUgYW54aWV0eSBvZiBhZ2luZyAoaGVuY2UsIHByb2JhYmx5IHRoZSByZWFzb24gZm9yIGEgbW9yZSB1cHBlci1taWRkbGUgdmFsZW5jZSkuIEFsYmVpdCwgd2hpbGUgZGVsaXZlcmluZyBhIHJlbGF0aXZlbHkgaGlnaCBhdmVyYWdlIGVuZXJneSBvZiAwLjcyNTMsICpBZnRlciBMYXVnaHRlciogZmFsbCAwLjE1IHVuaXRzIHNob3J0IG9mIHRoZWlyIGZpcnN0IGFsYnVtLCBzdWdnZXN0aW5nIGEgZmFsbCBpbiBwZXJjZWl2ZWQgbG91ZG5lc3MgYW5kIGdlbmVyYWwgZW50cm9weSBhdHRyaWJ1dGVkIHRvIHRoZSBiYW5kJ3MgZ2VucmUgc2hpZnQuIAoKVGhlIHRlcm5hcnkgcGxvdCBJIGNyZWF0ZWQgbm90IG9ubHkgY29tbXVuaWNhdGVzIHRoZXNlIGJyb2FkZXIgb2JzZXJ2YXRpb25zIHRha2VuIGZyb20gbXkgc3VtbWFyeSBzdGF0aXN0aWNzLCBidXQgYWxzbyBlbmFibGUgYSBkZWVwZXIgZGl2ZSBpbnRvIGVhY2ggYWxidW0gYXQgYW4gaW5kaXZpZHVhbCBzb25nIGxldmVsLiBDb250cmFzdGluZyB0aGUgdHdvIGFsYnVtcyBhdCBmaXJzdCBnbGFuY2UsIG5lYXJseSBhbGwgc29uZ3MgZnJvbSAqQWxsIFdlIEtub3cgaXMgRmFsbGluZyogYXJlIG1vcmUgaW4gdGhlIGRpcmVjdGlvbiBvZiB0aGUgRW5lcmd5IHZlcnRleCwgd2hlcmVhcyB3aXRoICpBZnRlciBMYXVnaHRlciosIHNvbmdzIGxlYW4gbW9yZSB0b3dhcmRzIHRoZSB2YWxlbmNlIGFuZCBkYW5jZWFiaWxpdHkgdmVydGV4J3MuIFdoaWxlIGl0J3MgaGFyZCB0byBkaWZmaWN1bHQgaW5kaXZpZHVkYWwgcG9pbnRzIGluIFIsIGluIElsbHVzdHJhdG9yLCBlbXBoYXNpcyB3YXMgZHJhd24gdG8gZWFjaCBpbmRpdmlkdWFsIHNvbmcgZnJvbSBlaXRoZXIgKkFmdGVyIExhdWdodGVyKiBvciAqQWxsIFdlIEtub3cgaXMgRmFsbGluZyogdGhhdCBoYWQgdGhlIG1heC9taW4gdmFsZW5jZSwgZGFuY2VhYmlsaXR5LCBhbmQgZW5lcmd5LiBIZXJlIGFyZSB0aG9zZSBzb25ncyB3aXRoIGEgcmVzcGVjdGl2ZSBxdW90ZSB0aGF0IGRyaXZlIHRoZWlyIGF3YXJkZWQgbW9vZHM6CgorIE1heCBWYWxlbmNlID0gIlJvc2UtQ29sb3JlZCBCb3kiIGZyb20gKkFmdGVyIExhdWdodGVyKiAKICAtICoqKiJIZXksIG1hbiwgd2UgYWxsIGNhbid0IGJlIGxpa2UgeW91LiBJIHdpc2ggd2Ugd2VyZSBhbGwgcm9zZS1jb2xvcmVkIHRvbyBteSByb3NlLWNvbG9yZWQgYm95IioqKgorIE1pbiBWYWxlbmNlID0gIkZyYW5rbGluIiBmcm9tICpBbGwgV2UgS25vdyBpcyBGYWxsaW5nKgogIC0gKioqIkl0J3MgdGFraW5nIHVwIG91ciB0aW1lIGFnYWluLiBHbyBiYWNrLCB3ZSBjYW4ndCBnbyBiYWNrIGF0IGFsbCIqKioKKyBNYXggRGFuY2VhYmlsaXR5ID0gIkNhdWdodCBpbiB0aGUgTWlkZGxlIiBmcm9tICpBZnRlciBMYXVnaHRlciogICAgIAogIC0gKioqIkknbSBqdXN0IGEgbGl0dGxlIGJpdCBjYXVnaHQgaW4gdGhlIG1pZGRsZS4gSSB0cnkgdG8ga2VlcCBnb2luZyBidXQgaXQncyBub3QgdGhhdCBzaW1wbGUiKioqCisgTWluIERhbmNlYWJpbGl0eSA9ICJXaG9hIiBmcm9tICpBbGwgV2UgS25vdyBpcyBGYWxsaW5nKgogIC0gKioqIkFuZCB3ZSBnb3QgZXZlcnlib2R5IHNpbmdpbmcgV2hvYSwgd2hvYS1vaCwgd2hvYSwgd2hvYS1vaCIqKioKKyBNYXggRW5lcmd5ID0gIkVtZXJnZW5jeSIgZnJvbSAqQWxsIFdlIEtub3cgaXMgRmFsbGluZyogCiAgLSAqKioiQ2F1c2UgSSd2ZSBzZWVuIGxvdmUgZGllIHdheSB0b28gbWFueSB0aW1lcyB3aGVuIGl0IGRlc2VydmVkIHRvIGJlIGFsaXZlIioqKgorIE1pbiBFbmVyZ3kgPSAiMjYiIGZyb20gKkFmdGVyIExhdWdodGVyKgogIC0gKioqIkhvbGQgb250byBob3BlLCBpZiB5b3UgZ290IGl0LiBEb24ndCBsZXQgaXQgZ28gZm9yIG5vYm9keSIqKioKCmBgYHtyIFN0YXRpc3RpY2FsIFRlc3RzfQoKIyBBTk9WQSAtIElzIHRoZXJlIGEgZGlmZmVyZW5jZSBpbiB0aGUgbWVhbnMgb2YgZWFjaCBtb29kIGF1ZGlvIGZlYXR1cmVzIGZvciBlYWNoIFBhcmFtb3JlIGFsYnVtPwoKIyBDcmVhdGUgQm94IFBsb3RzCnBhcmFtb3JlX2JveHBsb3RfdmFsZW5jZSA8LSBnZ3Bsb3QocGFyYW1vcmUsIGFlcyh4ID0gYWxidW1fbmFtZSAsIHkgPSB2YWxlbmNlKSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicyh0aXRsZSA9ICJBbGJ1bSBOYW1lIHZzIFZhbGVuY2UiLAogICAgICAgeCA9ICJBbGJ1bSBOYW1lIiwgeSA9ICJWYWxlbmNlIikgKwogIHRoZW1lX2J3KCkKIyBwYXJhbW9yZV9ib3hwbG90X3ZhbGVuY2UgIyB1c2VkIHRvIGNvcnJvYm9yYXRlIEFOT1ZBIHJlc3VsdCBpbnRlcnByZXRhdGlvbnMKCnBhcmFtb3JlX2JveHBsb3RfZGFuY2VhYmlsdHkgPC0gZ2dwbG90KHBhcmFtb3JlLCBhZXMoeCA9IGFsYnVtX25hbWUgLCB5ID0gZGFuY2VhYmlsaXR5KSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicyh0aXRsZSA9ICJBbGJ1bSBOYW1lIHZzIERhbmNlYWJpbGl0eSIsCiAgICAgICB4ID0gIkFsYnVtIE5hbWUiLCB5ID0gIkRhbmNlYWJpbGl0eSIpICsKICB0aGVtZV9idygpCiMgcGFyYW1vcmVfYm94cGxvdF9kYW5jZWFiaWx0eSAjIHVzZWQgdG8gY29ycm9ib3JhdGUgQU5PVkEgcmVzdWx0IGludGVycHJldGF0aW9ucwoKcGFyYW1vcmVfYm94cGxvdF9lbmVyZ3kgPC0gZ2dwbG90KHBhcmFtb3JlLCBhZXMoeCA9IGFsYnVtX25hbWUgLCB5ID0gZW5lcmd5KSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicyh0aXRsZSA9ICJBbGJ1bSBOYW1lIHZzIEVuZXJneSIsCiAgICAgICB4ID0gIkFsYnVtIE5hbWUiLCB5ID0gIkVuZXJneSIpICsKICB0aGVtZV9idygpCiMgcGFyYW1vcmVfYm94cGxvdF9lbmVyZ3kgIyB1c2VkIHRvIGNvcnJvYm9yYXRlIEFOT1ZBIHJlc3VsdCBpbnRlcnByZXRhdGlvbnMKCiMgUnVuIEFOT1ZBIFRlc3RzCmFub3ZhX3ZhbGVuY2UgPC0gbG0ocGFyYW1vcmUkdmFsZW5jZX5wYXJhbW9yZSRhbGJ1bV9uYW1lKQphbm92YShhbm92YV92YWxlbmNlKQoKYW5vdmFfZGFuY2VhYmlsaXR5IDwtIGxtKHBhcmFtb3JlJGRhbmNlYWJpbGl0eX5wYXJhbW9yZSRhbGJ1bV9uYW1lKQphbm92YShhbm92YV9kYW5jZWFiaWxpdHkpCgphbm92YV9lbmVyZ3kgPC0gbG0ocGFyYW1vcmUkZW5lcmd5fnBhcmFtb3JlJGFsYnVtX25hbWUpCmFub3ZhKGFub3ZhX2VuZXJneSkKYGBgCgpTaW5jZSB0aGUgUHIoPkYpIGlzIDAuMDE4MjEgPCAwLjA1LCB3ZSBhcmUgOTUlIGNvbmZpZGVudCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gdmFsZW5jZSBhY3Jvc3MgZGlmZmVyZW50IFBhcmFtb3JlIGFsYnVtczsgdGhpcyBpcyByZWZsZWN0ZWQgaW4gdGhlIGJveCBwbG90IGNyZWF0ZWQuIFNpbmNlIHRoZSBQcig+RikgaXMgMS4yODZlLTA4IDwgMC4wNSwgd2UgYXJlIDk1JSBjb25maWRlbnQgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIGRhbmNlYWJpbGl0eSBhY3Jvc3MgZGlmZmVyZW50IFBhcmFtb3JlIGFsYnVtczsgdGhpcyBpcyByZWZsZWN0ZWQgaW4gdGhlIGJveCBwbG90IGNyZWF0ZWQuIFNpbmNlIHRoZSBQcig+RikgaXMgMC4xMzggPiAwLjA1LCBhY2NlcHQgdGhlIG51bGwgaHlwb3RoZXNpcywgdGh1cyB0aGVyZSBpcyBub3QgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIGVuZXJneSBhY3Jvc3MgZGlmZmVyZW50IFBhcmFtb3JlIGFsYnVtczsgdGhpcyBpcyByZWZsZWN0ZWQgaW4gdGhlIGJveCBwbG90IGNyZWF0ZWQuIAoKLS0tLS0KCkZvciBhIGZ1dHVyZS91cGNvbWluZyBwZXJzb25hbCBwcm9qZWN0LCBpbnNwaXJlZCBieSBQYWJsbyBBbHZhcmV6J3MgW1JlZCBIb3QgQ2hpbGkgUGVwcGVycyBzZXRsaXN0IHZpc3VhbGl6YXRpb25dKGh0dHBzOi8vdHdpdHRlci5jb20vcGFibG9fYWx2cmV6L3N0YXR1cy8xNTMzNTI1OTkzNTM2Nzc0MTQ0L3Bob3RvLzEpLCBJIHdhbnQgdG8gbG9vayBhdCBob3cgYW4gYXJ0aXN0IHN0cnVjdHVyZXMgdGhlaXIgY29uY2VydCBmb3IgYSBuZXdseSByZWxlYXNlZCBhbGJ1bSBpbiByZWxhdGlvbiB0byB0aGUgb3JnaW5hbCBvcmRlciBvZiB0aGVpciBhbGJ1bS4gT24gSnVuZSAxNXRoLCBJJ2xsIGJlIGF0dGVuZGluZyBSYXZ5biBMZW5hZSdzIEh5cG5vcyBUb3VyIGluIExvbmRvbiBhbmQgam90dGluZyBkb3duIGhlciBzZXRsaXN0IGF0IHRoaXMgY29uY2VydC4gQWZ0ZXIsIEknbGwgYWltIHRvIGNyZWF0ZSBhIHZpc3VhbGl6YXRpb24gaW4gUiBjb21wYXJpbmcgdGhlIG9yaWdpbmFsIG9yZGVyIG9mIEh5cG5vcyB0byB0aGUgb3JkZXIgb2YgbXVzaWMgcHJlc2VudGVkIGF0IHRoZSBjb25jZXJ0LiBNeSBhaW0gd2lsbCBiZSB0byB1bmRlcnN0YW5kIHRoZSB0ZW1wb3JhbCBhbmQgbW9vZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGEgcmVjb3JkZWQgYWxidW0gYW5kIGEgbGl2ZSBwcmVzZW50YXRpb24gb2YgdGhhdCBhbGJ1bS4gVGhlIGZpcnN0IHBpZWNlIG9mIGNvZGUgYmVsb3cgY3JlYXRlcyBhIGRhdGEgZnJhbWUgd2l0aCBlYWNoIHRyYWNrIGZyb20gaHlwbm9zIGFuZCBpdHMgYXNzb2NpYXRlZCB0cmFjayBhdWRpbyBmZWF0dXJlcy4gSSB0aGVuIGFkZGVkIGEgY29sdW1uIHdpdGggZWFjaCB0cmFja3MgdGltZXMgaW4gc2Vjb25kcywgd2hpY2ggd2lsbCBhaWQgaW4gbGF0dGVyIHZpc3VhbGl6YXRpb24uCgpgYGB7ciBGVVRVUkUgUEVSU09OQUwgUFJPSkVDVH0KCiMgYWxidW0gbGluazogaHR0cHM6Ly9vcGVuLnNwb3RpZnkuY29tL2FsYnVtLzVZNGhVZDBGUHZDZWQ1bHU3bG9NWFo/c2k9ajBQaWxlbFhSRHk5N3U0cVNlMjNvZwoKIyBhY2Nlc3MgaHlwbm9zIHRyYWNrcyBhbmQgZmVhdHVyZXMKaHlwbm9zX3RyYWNrcyA8LSBnZXRfYWxidW1fdHJhY2tzKCI1WTRoVWQwRlB2Q2VkNWx1N2xvTVhaIikgCmh5cG5vc19mZWF0dXJlcyA8LSBnZXRfdHJhY2tfYXVkaW9fZmVhdHVyZXMoaHlwbm9zX3RyYWNrcyRpZCkKaHlwbm9zIDwtIGh5cG5vc190cmFja3MgJT4lCiAgbGVmdF9qb2luKGh5cG5vc19mZWF0dXJlcywgYnkgPSBjKCdpZCcgPSAnaWQnKSkgIyB0cmFja3MgZmllbGQgPSBmZWF0dXJlcyBmaWVsZCAKaGVhZChoeXBub3MpCgojIGNyZWF0ZSBhIGxpc3Qgb2YgdHJhY2sgZHVyYXRpb24gdmFsdWVzIChpbiBzZWNvbmRzKQpoeXBub3NfdHJhY2tfZHVyYXRpb24gPC0gYyg4NCwgMjA5LCAyNTEsIDIwNywgMjI3LCAyMjQsIDE5MiwgOTUsIDE4OSwgMjY1LCAyMzksIDIyOCwgMTEyLCAxNzgsIDI2MywgMjQzKQoKIyBhZGQgdHJhY2sgdGltZSBkdXJhdGlvbiAoaW4gc2Vjb25kcykgY29sdW1uIApoeXBub3MgPC0gaHlwbm9zICU+JQogIG11dGF0ZShoeXBub3NfdHJhY2tfZHVyYXRpb24pCmBgYAoKKlItU3R1ZGlvIFNob3J0IEN1dHMqCnRvIGluc2VydCBjaHVuazogKkNtZCtPcHRpb24rSSoKdG8gcHJldmlldyBtYXJrZG93bjogKkNtZCtTaGlmdCtLKgp0byBydW4gY29kZTogKkNtZCtTaGlmdCtFbnRlcioKCg==