Stack Overflow questions
We will analyze Stack Overflow questions, answers, and tags dataset.
This will include calculating and visualizing trends for some notable tags like dplyr and ggplot2.
library(readr)
library(tidyr)
Registered S3 method overwritten by 'dplyr':
method from
print.rowwise_df
library(dplyr)
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
questions <- read_rds(url("https://assets.datacamp.com/production/repositories/5284/datasets/89d5a716b4f41dbe4fcda1a7a1190f24f58f0e47/questions.rds"))
tags <- read_rds(url("https://assets.datacamp.com/production/repositories/5284/datasets/207c31b235786e73496fd7e58e416779911a9d98/tags.rds"))
question_tags <- read_rds(url("https://assets.datacamp.com/production/repositories/5284/datasets/966938d665c69bffd87393b345ea2837a94bab97/question_tags.rds"))
answers <- read_rds(url("https://assets.datacamp.com/production/repositories/5284/datasets/6cb9c039aa8326d98de37afefa32e1c458764638/answers.rds"))
head(questions)
head(tags)
head(question_tags)
head(answers)
Joining questions and answers
Finding gaps between questions and answers
Now we’ll join together questions with answers so we can measure the time between questions and answers.
questions %>%
# Inner join questions and answers with proper suffixes
inner_join(answers, c("id" = "question_id"), suffix = c("_question", "_answer")) %>%
# Subtract creation_date_question from creation_date_answer to create gap
mutate(gap = as.integer(creation_date_answer - creation_date_question))
Now we could use this information to identify how long it takes different questions to get answers.
Joining question and answer counts
We can also determine how many questions actually yield answers. If we count the number of answers for each question, we can then join the answers counts with the questions table.
# Count and sort the question id column in the answers table
answer_counts <- answers %>%
count(question_id, sort = TRUE)
# Combine the answer_counts and questions tables
question_answer_counts <- questions %>%
left_join(answer_counts, by = c("id" = "question_id"))%>%
# Replace the NAs in the n column
replace_na(list(n = 0))
head(question_answer_counts)
We can use this combined table to see which questions have the most answers, and which questions have no answers.
Average answers by question
We can use tagged_answers table to determine, on average, how many answers each questions gets.
Some of the important variables from this table include: n, the number of answers for each question, and tag_name, the name of each tag associated with each question.
tagged_answers %>%
# Aggregate by tag_name
group_by(tag_name) %>%
# Summarize questions and average_answers
summarize(questions = n(),
average_answers = mean(n)) %>%
# Sort the questions in descending order
arrange(desc(questions))
We can see if you post a question about ggplot2, on average you’ll get an answer.
The bind rows verb
Binding and counting posts with tags
First, we’ll want to combine these tables into a single table called posts_with_tags. Once the information is consolidated into a single table, we can add more information by creating a date variable using the lubridate package.
library(lubridate)
Attaching package: 㤼㸱lubridate㤼㸲
The following object is masked from 㤼㸱package:base㤼㸲:
date
# Combine the two tables into posts_with_tags
posts_with_tags <- bind_rows(questions_with_tags %>% mutate(type = "question"),
answers_with_tags %>% mutate(type = "answer"))
# Add a year column, then aggregate by type, year, and tag_name
by_type_year_tag <- posts_with_tags %>%
mutate(year = year(creation_date)) %>%
group_by(type, year, tag_name) %>%
count()
by_type_year_tag
LS0tDQp0aXRsZTogIkpvaW5zIG9uIFN0YWNrIE92ZXJmbG93IERhdGEiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCiMgU3RhY2sgT3ZlcmZsb3cgcXVlc3Rpb25zDQoNCldlIHdpbGwgYW5hbHl6ZSBTdGFjayBPdmVyZmxvdyBxdWVzdGlvbnMsIGFuc3dlcnMsIGFuZCB0YWdzIGRhdGFzZXQuDQoNClRoaXMgd2lsbCBpbmNsdWRlIGNhbGN1bGF0aW5nIGFuZCB2aXN1YWxpemluZyB0cmVuZHMgZm9yIHNvbWUgbm90YWJsZSB0YWdzIGxpa2UgZHBseXIgYW5kIGdncGxvdDIuDQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQpgYGB7cn0NCnF1ZXN0aW9ucyA8LSByZWFkX3Jkcyh1cmwoImh0dHBzOi8vYXNzZXRzLmRhdGFjYW1wLmNvbS9wcm9kdWN0aW9uL3JlcG9zaXRvcmllcy81Mjg0L2RhdGFzZXRzLzg5ZDVhNzE2YjRmNDFkYmU0ZmNkYTFhN2ExMTkwZjI0ZjU4ZjBlNDcvcXVlc3Rpb25zLnJkcyIpKQ0KdGFncyA8LSByZWFkX3Jkcyh1cmwoImh0dHBzOi8vYXNzZXRzLmRhdGFjYW1wLmNvbS9wcm9kdWN0aW9uL3JlcG9zaXRvcmllcy81Mjg0L2RhdGFzZXRzLzIwN2MzMWIyMzU3ODZlNzM0OTZmZDdlNThlNDE2Nzc5OTExYTlkOTgvdGFncy5yZHMiKSkNCnF1ZXN0aW9uX3RhZ3MgPC0gcmVhZF9yZHModXJsKCJodHRwczovL2Fzc2V0cy5kYXRhY2FtcC5jb20vcHJvZHVjdGlvbi9yZXBvc2l0b3JpZXMvNTI4NC9kYXRhc2V0cy85NjY5MzhkNjY1YzY5YmZmZDg3MzkzYjM0NWVhMjgzN2E5NGJhYjk3L3F1ZXN0aW9uX3RhZ3MucmRzIikpDQphbnN3ZXJzIDwtIHJlYWRfcmRzKHVybCgiaHR0cHM6Ly9hc3NldHMuZGF0YWNhbXAuY29tL3Byb2R1Y3Rpb24vcmVwb3NpdG9yaWVzLzUyODQvZGF0YXNldHMvNmNiOWMwMzlhYTgzMjZkOThkZTM3YWZlZmEzMmUxYzQ1ODc2NDYzOC9hbnN3ZXJzLnJkcyIpKQ0KYGBgDQoNCg0KDQoNCg0KYGBge3J9DQpoZWFkKHF1ZXN0aW9ucykNCmhlYWQodGFncykNCmhlYWQocXVlc3Rpb25fdGFncykNCmhlYWQoYW5zd2VycykNCmBgYA0KIyMgTGVmdC1qb2luaW5nIHF1ZXN0aW9ucyBhbmQgdGFncw0KDQpUaHJlZSBvZiB0aGUgU3RhY2sgT3ZlcmZsb3cgc3VydmV5IGRhdGFzZXRzIGFyZSBxdWVzdGlvbnMsIHF1ZXN0aW9uX3RhZ3MsIGFuZCB0YWdzOg0KDQpxdWVzdGlvbnM6IGFuIElEIGFuZCB0aGUgc2NvcmUsIG9yIGhvdyBtYW55IHRpbWVzIHRoZSBxdWVzdGlvbiBoYXMgYmVlbiB1cHZvdGVkOyB0aGUgZGF0YSBvbmx5IGluY2x1ZGVzIFItYmFzZWQgcXVlc3Rpb25zDQpxdWVzdGlvbl90YWdzOiBhIHRhZyBJRCBmb3IgZWFjaCBxdWVzdGlvbiBhbmQgdGhlIHF1ZXN0aW9uJ3MgaWQNCnRhZ3M6IGEgdGFnIGlkIGFuZCB0aGUgdGFnJ3MgbmFtZSwgd2hpY2ggY2FuIGJlIHVzZWQgdG8gaWRlbnRpZnkgdGhlIHN1YmplY3Qgb2YgZWFjaCBxdWVzdGlvbiwgc3VjaCBhcyBnZ3Bsb3QyIG9yIGRwbHlyDQpJbiB0aGlzIGV4ZXJjaXNlLCB3ZSdsbCBiZSBzdGl0Y2hpbmcgdG9nZXRoZXIgdGhlc2UgZGF0YXNldHMgYW5kIHJlcGxhY2luZyBOQXMgaW4gaW1wb3J0YW50IGZpZWxkcy4NCg0KTm90ZSB0aGF0IHdlJ2xsIGJlIHVzaW5nIGxlZnRfam9pbnMgaW4gdGhpcyBleGVyY2lzZSB0byBlbnN1cmUgd2Uga2VlcCBhbGwgcXVlc3Rpb25zLCBldmVuIHRob3NlIHdpdGhvdXQgYSBjb3JyZXNwb25kaW5nIHRhZy4gSG93ZXZlciwgc2luY2Ugd2Uga25vdyB0aGUgcXVlc3Rpb25zIGRhdGEgaXMgYWxsIFIgZGF0YSwgd2UnbGwgd2FudCB0byBtYW51YWxseSB0YWcgdGhlc2UgYXMgUiBxdWVzdGlvbnMgd2l0aCByZXBsYWNlX25hLg0KYGBge3J9DQojIEpvaW4gdGhlIHF1ZXN0aW9ucyBhbmQgcXVlc3Rpb25fdGFncyB0YWJsZXMNCnF1ZXN0aW9uc193aXRoX3RhZ3MgPC0gcXVlc3Rpb25zICU+JQ0KICBsZWZ0X2pvaW4ocXVlc3Rpb25fdGFncywgYnkgPSBjKCJpZCIgPSAicXVlc3Rpb25faWQiKSkgJT4lDQogICMgSm9pbiB0aGUgdGFncyBhcyB3ZWxsDQogIGxlZnRfam9pbih0YWdzLCBieSA9IGMoInRhZ19pZCIgPSAiaWQiKSkgJT4lDQogICMgUmVwbGFjZSB0aGUgTkFzIGluIHRoZSB0YWdfbmFtZSBjb2x1bW4NCiAgcmVwbGFjZV9uYShsaXN0KHRhZ19uYW1lID0gIm9ubHktciIpKQ0KDQpoZWFkKHF1ZXN0aW9uc193aXRoX3RhZ3MpDQpgYGANCldlIG5vdyBoYXZlIGEgZGF0YXNldCB0aGF0IHdlIGNhbiBhbmFseXplIGFmdGVyIGFsbCB0aGF0IGpvaW5pbmcuDQoNCiMjIENvbXBhcmluZyBzY29yZXMgYWNyb3NzIHRhZ3MNCg0KTGV0J3MgZG8gYSBxdWljayBiaXQgb2YgYW5hbHlzaXMgb24gaXQhIFdlJ2xsIHVzZSBmYW1pbGlhciBkcGx5ciB2ZXJicyBsaWtlIGdyb3VwX2J5LCBzdW1tYXJpemUsIGFycmFuZ2UsIGFuZCBuIHRvIGZpbmQgb3V0IHRoZSBhdmVyYWdlIHNjb3JlIG9mIHRoZSBtb3N0IGFza2VkIHF1ZXN0aW9ucy4NCmBgYHtyfQ0KcXVlc3Rpb25zX3dpdGhfdGFncyAlPiUNCgkjIEdyb3VwIGJ5IHRhZ19uYW1lDQogIGdyb3VwX2J5KHRhZ19uYW1lKSAlPiUNCgkjIEdldCBtZWFuIHNjb3JlIGFuZCBudW1fcXVlc3Rpb25zDQoJc3VtbWFyaXplKHNjb3JlID0gbWVhbihzY29yZSksDQogICAgICAgICAgCSAgbnVtX3F1ZXN0aW9ucyA9IG4oKSkgJT4lDQoJIyBTb3J0IG51bV9xdWVzdGlvbnMgaW4gZGVzY2VuZGluZyBvcmRlcg0KCWFycmFuZ2UoZGVzYyhudW1fcXVlc3Rpb25zKSkNCmBgYA0KSXQgbG9va3MgbGlrZSBxdWVzdGlvbnMgd2l0aCB0aGUgUiB0YWcgZ2V0IGEgcmVsYXRpdmVseSBsb3cgc2NvcmUsIGJ1dCBxdWVzdGlvbnMgd2l0aCB0aGUgbG9vcHMgdGFnIGFyZSBldmVuIGxvd2VyLg0KDQojIyMgV2hhdCB0YWdzIG5ldmVyIGFwcGVhciBvbiBSIHF1ZXN0aW9ucz8NCg0KVGhlIHRhZ3MgdGFibGUgaW5jbHVkZXMgYWxsIFN0YWNrIE92ZXJmbG93IHRhZ3MsIGJ1dCBzb21lIGhhdmUgbm90aGluZyB0byBkbyB3aXRoIFIuIEhvdyBjb3VsZCB5b3UgZmlsdGVyIGZvciBqdXN0IHRoZSB0YWdzIHRoYXQgbmV2ZXIgYXBwZWFyIG9uIGFuIFIgcXVlc3Rpb24/DQpgYGB7cn0NCiMgVXNpbmcgYSBqb2luLCBmaWx0ZXIgZm9yIHRhZ3MgdGhhdCBhcmUgbmV2ZXIgb24gYW4gUiBxdWVzdGlvbg0KdGFncyAlPiUNCiAgYW50aV9qb2luKHF1ZXN0aW9uX3RhZ3MsIGMoImlkIiA9ICJ0YWdfaWQiKSkgJT4lDQogIGZpbHRlcih0YWdfbmFtZSAhPSAib25seS1yIikNCmBgYA0KSXQgbG9va3MgbGlrZSB0aGVyZSBhcmUgbW9yZSB0aGFuIDQwLDAwMCB0YWdzIHRoYXQgaGF2ZSBuZXZlciBhcHBlYXJlZCBhbG9uZyBSIQ0KDQojIEpvaW5pbmcgcXVlc3Rpb25zIGFuZCBhbnN3ZXJzDQoNCiMjIEZpbmRpbmcgZ2FwcyBiZXR3ZWVuIHF1ZXN0aW9ucyBhbmQgYW5zd2Vycw0KDQpOb3cgd2UnbGwgam9pbiB0b2dldGhlciBxdWVzdGlvbnMgd2l0aCBhbnN3ZXJzIHNvIHdlIGNhbiBtZWFzdXJlIHRoZSB0aW1lIGJldHdlZW4gcXVlc3Rpb25zIGFuZCBhbnN3ZXJzLg0KYGBge3J9DQpxdWVzdGlvbnMgJT4lDQoJIyBJbm5lciBqb2luIHF1ZXN0aW9ucyBhbmQgYW5zd2VycyB3aXRoIHByb3BlciBzdWZmaXhlcw0KCWlubmVyX2pvaW4oYW5zd2VycywgYygiaWQiID0gInF1ZXN0aW9uX2lkIiksIHN1ZmZpeCA9IGMoIl9xdWVzdGlvbiIsICJfYW5zd2VyIikpICU+JQ0KCSMgU3VidHJhY3QgY3JlYXRpb25fZGF0ZV9xdWVzdGlvbiBmcm9tIGNyZWF0aW9uX2RhdGVfYW5zd2VyIHRvIGNyZWF0ZSBnYXANCgltdXRhdGUoZ2FwID0gYXMuaW50ZWdlcihjcmVhdGlvbl9kYXRlX2Fuc3dlciAtIGNyZWF0aW9uX2RhdGVfcXVlc3Rpb24pKQ0KYGBgDQpOb3cgd2UgY291bGQgdXNlIHRoaXMgaW5mb3JtYXRpb24gdG8gaWRlbnRpZnkgaG93IGxvbmcgaXQgdGFrZXMgZGlmZmVyZW50IHF1ZXN0aW9ucyB0byBnZXQgYW5zd2Vycy4NCg0KIyMgSm9pbmluZyBxdWVzdGlvbiBhbmQgYW5zd2VyIGNvdW50cw0KDQpXZSBjYW4gYWxzbyBkZXRlcm1pbmUgaG93IG1hbnkgcXVlc3Rpb25zIGFjdHVhbGx5IHlpZWxkIGFuc3dlcnMuIElmIHdlIGNvdW50IHRoZSBudW1iZXIgb2YgYW5zd2VycyBmb3IgZWFjaCBxdWVzdGlvbiwgd2UgY2FuIHRoZW4gam9pbiB0aGUgYW5zd2VycyBjb3VudHMgd2l0aCB0aGUgcXVlc3Rpb25zIHRhYmxlLg0KYGBge3J9DQojIENvdW50IGFuZCBzb3J0IHRoZSBxdWVzdGlvbiBpZCBjb2x1bW4gaW4gdGhlIGFuc3dlcnMgdGFibGUNCmFuc3dlcl9jb3VudHMgPC0gYW5zd2VycyAlPiUNCiAgY291bnQocXVlc3Rpb25faWQsIHNvcnQgPSBUUlVFKQ0KDQojIENvbWJpbmUgdGhlIGFuc3dlcl9jb3VudHMgYW5kIHF1ZXN0aW9ucyB0YWJsZXMNCnF1ZXN0aW9uX2Fuc3dlcl9jb3VudHMgPC0gcXVlc3Rpb25zICU+JQ0KCWxlZnRfam9pbihhbnN3ZXJfY291bnRzLCBieSA9IGMoImlkIiA9ICJxdWVzdGlvbl9pZCIpKSU+JQ0KCSMgUmVwbGFjZSB0aGUgTkFzIGluIHRoZSBuIGNvbHVtbg0KICByZXBsYWNlX25hKGxpc3QobiA9IDApKQ0KDQpoZWFkKHF1ZXN0aW9uX2Fuc3dlcl9jb3VudHMpDQpgYGANCldlIGNhbiB1c2UgdGhpcyBjb21iaW5lZCB0YWJsZSB0byBzZWUgd2hpY2ggcXVlc3Rpb25zIGhhdmUgdGhlIG1vc3QgYW5zd2VycywgYW5kIHdoaWNoIHF1ZXN0aW9ucyBoYXZlIG5vIGFuc3dlcnMuDQoNCiMjIEpvaW5pbmcgcXVlc3Rpb25zLCBhbnN3ZXJzLCBhbmQgdGFncw0KDQpMZXQncyBidWlsZCBvbiB0aGUgbGFzdCBleGVyY2lzZSBieSBhZGRpbmcgdGhlIHRhZ3MgdGFibGUgdG8gb3VyIHByZXZpb3VzIGpvaW5zLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gZG8gYSBiZXR0ZXIgam9iIG9mIGlkZW50aWZ5aW5nIHdoaWNoIFIgdG9waWNzIGdldCB0aGUgbW9zdCB0cmFjdGlvbiBvbiBTdGFjayBPdmVyZmxvdy4NCmBgYHtyfQ0KdGFnZ2VkX2Fuc3dlcnMgPC0gcXVlc3Rpb25fYW5zd2VyX2NvdW50cyAlPiUNCgkjIEpvaW4gdGhlIHF1ZXN0aW9uX3RhZ3MgdGFibGVzDQoJaW5uZXJfam9pbihxdWVzdGlvbl90YWdzLCBieSA9IGMoImlkIiA9ICJxdWVzdGlvbl9pZCIpKSAlPiUNCgkjIEpvaW4gdGhlIHRhZ3MgdGFibGUNCiAgaW5uZXJfam9pbih0YWdzLCBieSA9IGMoInRhZ19pZCIgPSAiaWQiKSkNCnRhZ2dlZF9hbnN3ZXJzDQpgYGANCk5vdyB3ZSBoYXZlIGEgbW9yZSBob2xpc3RpYyB2aWV3IG9mIGhvdyBxdWVzdGlvbnMgYXJlIGFuc3dlcmVkIGJ5IGVhY2ggdGFnLg0KDQojIyBBdmVyYWdlIGFuc3dlcnMgYnkgcXVlc3Rpb24NCg0KV2UgY2FuIHVzZSB0YWdnZWRfYW5zd2VycyB0YWJsZSB0byBkZXRlcm1pbmUsIG9uIGF2ZXJhZ2UsIGhvdyBtYW55IGFuc3dlcnMgZWFjaCBxdWVzdGlvbnMgZ2V0cy4NCg0KU29tZSBvZiB0aGUgaW1wb3J0YW50IHZhcmlhYmxlcyBmcm9tIHRoaXMgdGFibGUgaW5jbHVkZTogbiwgdGhlIG51bWJlciBvZiBhbnN3ZXJzIGZvciBlYWNoIHF1ZXN0aW9uLCBhbmQgdGFnX25hbWUsIHRoZSBuYW1lIG9mIGVhY2ggdGFnIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHF1ZXN0aW9uLg0KYGBge3J9DQp0YWdnZWRfYW5zd2VycyAlPiUNCgkjIEFnZ3JlZ2F0ZSBieSB0YWdfbmFtZQ0KICBncm91cF9ieSh0YWdfbmFtZSkgJT4lIA0KCSMgU3VtbWFyaXplIHF1ZXN0aW9ucyBhbmQgYXZlcmFnZV9hbnN3ZXJzDQogICAgc3VtbWFyaXplKHF1ZXN0aW9ucyA9IG4oKSwNCiAgICAgICAgICAgICAgYXZlcmFnZV9hbnN3ZXJzID0gbWVhbihuKSkgJT4lDQoJIyBTb3J0IHRoZSBxdWVzdGlvbnMgaW4gZGVzY2VuZGluZyBvcmRlcg0KCWFycmFuZ2UoZGVzYyhxdWVzdGlvbnMpKQ0KYGBgDQpXZSBjYW4gc2VlIGlmIHlvdSBwb3N0IGEgcXVlc3Rpb24gYWJvdXQgZ2dwbG90Miwgb24gYXZlcmFnZSB5b3UnbGwgZ2V0IGFuIGFuc3dlci4NCg0KIyBUaGUgYmluZCByb3dzIHZlcmINCg0KIyMgSm9pbmluZyBxdWVzdGlvbnMgYW5kIGFuc3dlcnMgd2l0aCB0YWdzDQoNClRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlIHF1ZXN0aW9ucyBhbmQgYW5zd2VycyB0YWJsZSwgd2UnbGwgd2FudCB0byB1c2UgdGhlIHF1ZXN0aW9uX3RhZ3MgdGFibGUgdG8gdW5kZXJzdGFuZCB0aGUgdGFncyBhc3NvY2lhdGVkIHdpdGggZWFjaCBxdWVzdGlvbiB0aGF0IHdhcyBhc2tlZCwgYW5kIGVhY2ggYW5zd2VyIHRoYXQgd2FzIHByb3ZpZGVkLiBXZSdsbCBiZSBhYmxlIHRvIGNvbWJpbmUgdGhlc2UgdGFibGVzIHVzaW5nIHR3byBpbm5lciBqb2lucyBvbiBib3RoIHRoZSBxdWVzdGlvbnMgdGFibGUgYW5kIHRoZSBhbnN3ZXJzIHRhYmxlLg0KDQpgYGB7cn0NCiMgSW5uZXIgam9pbiB0aGUgcXVlc3Rpb25fdGFncyBhbmQgdGFncyB0YWJsZXMgd2l0aCB0aGUgcXVlc3Rpb25zIHRhYmxlDQpxdWVzdGlvbnNfd2l0aF90YWdzIDwtIHF1ZXN0aW9ucyAlPiUNCiAgaW5uZXJfam9pbihxdWVzdGlvbl90YWdzLCBieSA9IGMoImlkIiA9ICJxdWVzdGlvbl9pZCIpKSAlPiUNCiAgaW5uZXJfam9pbih0YWdzLCBieSA9IGMoInRhZ19pZCIgPSAiaWQiKSkNCnF1ZXN0aW9uc193aXRoX3RhZ3MNCiMgSW5uZXIgam9pbiB0aGUgcXVlc3Rpb25fdGFncyBhbmQgdGFncyB0YWJsZXMgd2l0aCB0aGUgYW5zd2VycyB0YWJsZQ0KYW5zd2Vyc193aXRoX3RhZ3MgPC0gYW5zd2VycyAlPiUNCiAgaW5uZXJfam9pbihxdWVzdGlvbl90YWdzLCBieSA9IGMoInF1ZXN0aW9uX2lkIiA9ICJxdWVzdGlvbl9pZCIpKSAlPiUNCiAgaW5uZXJfam9pbih0YWdzLCBieSA9IGMoInRhZ19pZCIgPSAiaWQiKSkNCmFuc3dlcnNfd2l0aF90YWdzDQpgYGANCk5vdyB3ZSB3aWxsIGJlIGFibGUgdG8gY29tYmluZSBlYWNoIG9mIHRoZXNlIGluZGl2aWR1YWwgdGFibGVzIGludG8gYSBzaW5nbGUgY29oZXNpdmUgdGFibGUgdG8gaGF2ZSBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBpbmZvcm1hdGlvbiB3ZSBoYXZlIGFib3V0IHRoZSBxdWVzdGlvbnMsIGFuc3dlcnMsIGFuZCBhc3NvY2lhdGVkIHRhZ3MuDQoNCiMjIEJpbmRpbmcgYW5kIGNvdW50aW5nIHBvc3RzIHdpdGggdGFncw0KDQpGaXJzdCwgd2UnbGwgd2FudCB0byBjb21iaW5lIHRoZXNlIHRhYmxlcyBpbnRvIGEgc2luZ2xlIHRhYmxlIGNhbGxlZCBwb3N0c193aXRoX3RhZ3MuIE9uY2UgdGhlIGluZm9ybWF0aW9uIGlzIGNvbnNvbGlkYXRlZCBpbnRvIGEgc2luZ2xlIHRhYmxlLCB3ZSBjYW4gYWRkIG1vcmUgaW5mb3JtYXRpb24gYnkgY3JlYXRpbmcgYSBkYXRlIHZhcmlhYmxlIHVzaW5nIHRoZSBsdWJyaWRhdGUgcGFja2FnZS4NCg0KYGBge3J9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCiMgQ29tYmluZSB0aGUgdHdvIHRhYmxlcyBpbnRvIHBvc3RzX3dpdGhfdGFncw0KcG9zdHNfd2l0aF90YWdzIDwtIGJpbmRfcm93cyhxdWVzdGlvbnNfd2l0aF90YWdzICU+JSBtdXRhdGUodHlwZSA9ICJxdWVzdGlvbiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5zd2Vyc193aXRoX3RhZ3MgJT4lIG11dGF0ZSh0eXBlID0gImFuc3dlciIpKQ0KDQoNCiMgQWRkIGEgeWVhciBjb2x1bW4sIHRoZW4gYWdncmVnYXRlIGJ5IHR5cGUsIHllYXIsIGFuZCB0YWdfbmFtZQ0KYnlfdHlwZV95ZWFyX3RhZyA8LSBwb3N0c193aXRoX3RhZ3MgJT4lDQogIG11dGF0ZSh5ZWFyID0geWVhcihjcmVhdGlvbl9kYXRlKSkgJT4lDQogIGdyb3VwX2J5KHR5cGUsIHllYXIsIHRhZ19uYW1lKSAlPiUNCiAgY291bnQoKQ0KYnlfdHlwZV95ZWFyX3RhZw0KYGBgDQojIyBWaXN1YWxpemluZyBxdWVzdGlvbnMgYW5kIGFuc3dlcnMgaW4gdGFncw0KDQpMZXQncyBjcmVhdGUgYSBwbG90IHRvIGV4YW1pbmUgdGhlIGluZm9ybWF0aW9uIHRoYXQgdGhlIHRhYmxlIGNvbnRhaW5zIGFib3V0IHF1ZXN0aW9ucyBhbmQgYW5zd2VycyBmb3IgdGhlIGRwbHlyIGFuZCBnZ3Bsb3QyIHRhZ3MuIA0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQojIEZpbHRlciBmb3IgdGhlIGRwbHlyIGFuZCBnZ3Bsb3QyIHRhZyBuYW1lcyANCmJ5X3R5cGVfeWVhcl90YWdfZmlsdGVyZWQgPC0gYnlfdHlwZV95ZWFyX3RhZyAlPiUNCiAgZmlsdGVyKHRhZ19uYW1lID09ICJkcGx5ciIgfCB0YWdfbmFtZSA9PSAiZ2dwbG90MiIpDQoNCiMgQ3JlYXRlIGEgbGluZSBwbG90IGZhY2V0ZWQgYnkgdGhlIHRhZyBuYW1lIA0KZ2dwbG90KGJ5X3R5cGVfeWVhcl90YWdfZmlsdGVyZWQsIGFlcyh4ID0geWVhciwgeSA9IG4sIGNvbG9yID0gdHlwZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBmYWNldF93cmFwKH4gdGFnX25hbWUpDQoNCmBgYA0KTm90aWNlIGFuc3dlcnMgb24gZHBseXIgcXVlc3Rpb25zIGFyZSBncm93aW5nIGZhc3RlciB0aGFuIGRwbHlyIHF1ZXN0aW9ucyB0aGVtc2VsdmVzOyBtZWFuaW5nIHRoZSBhdmVyYWdlIGRwbHlyIHF1ZXN0aW9uIGhhcyBtb3JlIGFuc3dlcnMgdGhhbiB0aGUgYXZlcmFnZSBnZ3Bsb3QyIHF1ZXN0aW9uLg0KDQoNCg==