You just got hired as a Data Analyst at the Census Bureau, which
collects census data and finds interesting insights from it.
The person who previously had your job left you all the data they had
for the most recent census. The data is spread across multiple csv
files. They didn’t use R, and they would manually look through these csv
files whenever they wanted to find something. Sometimes they would copy
and paste certain numbers into Excel for analysis.
The thought of it makes you shiver. This is not scalable or
repeatable.
Your boss wants you to dig into the data and find some insights by
the end of the day. Can you get this data into R and into reasonable
shape so that you can perform your analysis?
If you get stuck or want to see an experienced programmer tackle this
project, click “get help” to see a walkthrough video.
Load and Inspect the Data!
1.Open some of the census csv files in the navigator. How are they
named? What kind of information do they hold? What tools will you need
to import and clean this data?
2.In the first code block of notebook.Rmd, import the readr, dplyr,
and tidyr libraries to aid you in your data cleaning.
# load libraries
library(dplyr)
library(tidyr)
library(stringr)
3.It will be easier to inspect the data stored in these files once
you have it in a data frame. You can’t even call head() on these csvs!
How are you supposed to read them?
Begin by creating a variable called files and set it equal to the
list.files() of all of the csv files you want to import.
# load CSVs
files <- list.files(pattern = "states_.*csv")
files
[1] "states_0.csv" "states_1.csv" "states_2.csv" "states_3.csv" "states_4.csv" "states_5.csv" "states_6.csv"
[8] "states_7.csv" "states_8.csv" "states_9.csv"
4.Read each file in files into a data frame using lapply() and save
the result to df_list.
# inspect data
df_list <- lapply(files, read.csv)
df_list
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
[[9]]
[[10]]
NA
5.Concatenate all of the data frames in df_list into one data frame
called us_census.
# inspect data
us_census <- bind_rows(df_list)
us_census
6.Inspect the us_census data frame by printing the column names,
looking at the data types with str(), and viewing the head().
What columns have symbols that will prevent calculations? What are
the data types of the columns? Do any columns contain multiple kinds of
information?
# inspect data
col <- colnames(us_census)
col
[1] "X" "State" "TotalPop" "Hispanic" "White" "Black" "Native" "Asian" "Pacific"
[10] "Income" "GenderPop"
str(us_census)
'data.frame': 61 obs. of 11 variables:
$ X : int 0 1 2 3 4 5 0 1 2 3 ...
$ State : chr "Alabama" "Alaska" "Arizona" "Arkansas" ...
$ TotalPop : int 4830620 733375 6641928 2958208 38421464 5278906 5278906 3593222 926454 647484 ...
$ Hispanic : chr "3.7516156462584975%" "5.909580838323351%" "29.565921052631502%" "6.215474452554738%" ...
$ White : chr "61.878656462585%" "60.910179640718574%" "57.120000000000026%" "71.13781021897813%" ...
$ Black : chr "31.25297619047618%" "2.8485029940119775%" "3.8509868421052658%" "18.968759124087573%" ...
$ Native : chr "0.4532312925170065%" "16.39101796407186%" "4.35506578947368%" "0.5229197080291965%" ...
$ Asian : chr "1.0502551020408146%" "5.450299401197604%" "2.876578947368419%" "1.1423357664233578%" ...
$ Pacific : chr "0.03435374149659865%" "1.0586826347305378%" "0.16763157894736833%" "0.14686131386861315%" ...
$ Income : chr "$43296.35860306644" "$70354.74390243902" "$54207.82095490716" "$41935.63396778917" ...
$ GenderPop: chr "2341093M_2489527F" "384160M_349215F" "3299088M_3342840F" "1451913M_1506295F" ...
head(us_census)
Remove and Reformat the Columns
7.When inspecting us_census you notice a column X1 that stores
meaningless information. Drop the X1 column from us_census, and save the
resulting data frame to us_census. View the head of us_census.
# drop X1 column
us_census <- us_census %>%
select(-X)
us_census
8.You notice that there are 6 columns representing the population
percentage for different races. The columns include the percent symbol
%. Remove the percent symbol % from each of the race columns
(Hispanic,White,Black,Native,Asian,Pacific). Save the resulting data
frame to us_census, and view the head.
# remove % from race columns
us_census <- us_census %>%
mutate(Hispanic = gsub('%', '',Hispanic), White = gsub('%', '',Hispanic), Black = gsub('%', '',Hispanic), Native = gsub('%', '',Hispanic), Asian = gsub('%', '',Hispanic), Pacific = gsub('%', '',Hispanic))
head(us_census)
9.The Income column also incudes a $ symbol along with the number
representing median income for a state. Remove the $ from the Income
column. Save the resulting data frame to us_census. View the head of
us_census.
# remove $ from Income column
#gsub('$', '', Income) 這其實是「行尾」,不是「$ 符號」
#gsub('$', '', Income) 中的 '$' 是正則表達式中的特殊字元,它代表「行尾」,不是字面上的 $ 字符。
us_census <- us_census %>%
mutate(Income = gsub('\\$', '', Income))
head(us_census)
10.The GenderPop column appears to hold the male and female
population counts. Separate this column at the _ character to create two
new columns: male_pop and female_pop. Save the resulting data frame to
us_census, and view the head.
# separate GenderPop column
us_census <- us_census %>%
separate('GenderPop', c('male_pop', 'female_pop'), "_")
head(us_census)
11.You notice the new male_pop and female_pop columns contain extra
characters M and F, respectively. Remove these extra characters from the
columns. Save the resulting data frame to us_census, and view the
head.
# clean male and female population columns
us_census <- us_census %>%
mutate(male_pop = gsub('M', '', male_pop), female_pop = gsub('F', '', female_pop))
head(us_census)
Update the Data Types
12.Now that you have removed extra symbols from many of the columns
that contain numerical data, you notice that the data type for these
columns is still chr, or character. Convert all of these columns
(Hispanic,White,Black,Native,Asian,Pacific,Income,male_pop,female_pop)
to have a data type of numeric. Save the resulting data frame to
us_census, and view the head.
# update column data types
us_census <- us_census %>%
mutate(Hispanic = as.numeric(Hispanic), White = as.numeric(White), Black = as.numeric(Black), Native = as.numeric(Native), Asian = as.numeric(Asian), Pacific = as.numeric(Pacific), Income = as.numeric(Income), male_pop = as.numeric(male_pop), female_pop = as.numeric(female_pop))
head(us_census)
13.Take a second to look back at the Hispanic, White, Black, Native,
Asian, and Pacific columns. The columns represent the population
percentage for each race. Earlier you removed the % symbol, and then you
just converted the column to numeric type. To make calculations easier,
the column should now represent percentages in decimal form, where 50%
is equivalent to 0.50. Update the values of these columns to be in
decimal form, and save the resulting data frame to us_census. View the
head of us_census.
# update values of race columns
us_census <- us_census %>%
mutate(Hispanic = Hispanic / 100, White = White / 100, Black = Black / 100, Native = Native / 100, Asian = Asian / 100, Pacific = Pacific / 100)
head(us_census)
Remove Duplicate Rows
14.It’s always a good idea to check if there are duplicate rows of
data in a data set. Pipe us_census into the duplicated() function to see
which rows are duplicated. Then pipe the result into table() to get a
count of the duplicated rows.
# check for duplicate rows
duplicate <- us_census %>%
duplicated() %>%
table()
duplicate
.
FALSE TRUE
52 9
15.Since there are duplicates, update the value of us_census to be
the us_census data frame with only unique/distinct rows.
# remove duplicate rows
us_census <- us_census %>%
distinct()
us_census
16.Confirm that there are no more duplicated rows in us_census. Pipe
us_census into the duplicated() function to see which rows are
duplicated. Then pipe the result into table() to get a count of the
duplicated rows.
You should expect to see no TRUEs!
# check for duplicate rows
duplicate <- us_census %>%
duplicated() %>%
table()
duplicate
.
FALSE
52
17.View the head() of us_census. The data frame is all clean and
ready for analysis! What do you want to find out?
# clean data frame
head(us_census)
LS0tDQp0aXRsZTogIkNsZWFuaW5nIFVTIENlbnN1cyBEYXRhIg0KYXV0aG9yOiAiQW5uYWJlbCBLdW8iDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclWS0lbS0lZCAlSDolTScpYCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpZb3UganVzdCBnb3QgaGlyZWQgYXMgYSBEYXRhIEFuYWx5c3QgYXQgdGhlIENlbnN1cyBCdXJlYXUsIHdoaWNoIGNvbGxlY3RzIGNlbnN1cyBkYXRhIGFuZCBmaW5kcyBpbnRlcmVzdGluZyBpbnNpZ2h0cyBmcm9tIGl0Lg0KDQpUaGUgcGVyc29uIHdobyBwcmV2aW91c2x5IGhhZCB5b3VyIGpvYiBsZWZ0IHlvdSBhbGwgdGhlIGRhdGEgdGhleSBoYWQgZm9yIHRoZSBtb3N0IHJlY2VudCBjZW5zdXMuIFRoZSBkYXRhIGlzIHNwcmVhZCBhY3Jvc3MgbXVsdGlwbGUgY3N2IGZpbGVzLiBUaGV5IGRpZG7igJl0IHVzZSBSLCBhbmQgdGhleSB3b3VsZCBtYW51YWxseSBsb29rIHRocm91Z2ggdGhlc2UgY3N2IGZpbGVzIHdoZW5ldmVyIHRoZXkgd2FudGVkIHRvIGZpbmQgc29tZXRoaW5nLiBTb21ldGltZXMgdGhleSB3b3VsZCBjb3B5IGFuZCBwYXN0ZSBjZXJ0YWluIG51bWJlcnMgaW50byBFeGNlbCBmb3IgYW5hbHlzaXMuDQoNClRoZSB0aG91Z2h0IG9mIGl0IG1ha2VzIHlvdSBzaGl2ZXIuIFRoaXMgaXMgbm90IHNjYWxhYmxlIG9yIHJlcGVhdGFibGUuDQoNCllvdXIgYm9zcyB3YW50cyB5b3UgdG8gZGlnIGludG8gdGhlIGRhdGEgYW5kIGZpbmQgc29tZSBpbnNpZ2h0cyBieSB0aGUgZW5kIG9mIHRoZSBkYXkuIENhbiB5b3UgZ2V0IHRoaXMgZGF0YSBpbnRvIFIgYW5kIGludG8gcmVhc29uYWJsZSBzaGFwZSBzbyB0aGF0IHlvdSBjYW4gcGVyZm9ybSB5b3VyIGFuYWx5c2lzPw0KDQpJZiB5b3UgZ2V0IHN0dWNrIG9yIHdhbnQgdG8gc2VlIGFuIGV4cGVyaWVuY2VkIHByb2dyYW1tZXIgdGFja2xlIHRoaXMgcHJvamVjdCwgY2xpY2sg4oCcZ2V0IGhlbHDigJ0gdG8gc2VlIGEgd2Fsa3Rocm91Z2ggdmlkZW8uDQoNCiMgTG9hZCBhbmQgSW5zcGVjdCB0aGUgRGF0YSENCg0KMS5PcGVuIHNvbWUgb2YgdGhlIGNlbnN1cyBjc3YgZmlsZXMgaW4gdGhlIG5hdmlnYXRvci4gSG93IGFyZSB0aGV5IG5hbWVkPyBXaGF0IGtpbmQgb2YgaW5mb3JtYXRpb24gZG8gdGhleSBob2xkPyBXaGF0IHRvb2xzIHdpbGwgeW91IG5lZWQgdG8gaW1wb3J0IGFuZCBjbGVhbiB0aGlzIGRhdGE/DQoNCjIuSW4gdGhlIGZpcnN0IGNvZGUgYmxvY2sgb2Ygbm90ZWJvb2suUm1kLCBpbXBvcnQgdGhlIHJlYWRyLCBkcGx5ciwgYW5kIHRpZHlyIGxpYnJhcmllcyB0byBhaWQgeW91IGluIHlvdXIgZGF0YSBjbGVhbmluZy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXJyb3I9VFJVRX0NCiMgbG9hZCBsaWJyYXJpZXMNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KYGBgDQoNCjMuSXQgd2lsbCBiZSBlYXNpZXIgdG8gaW5zcGVjdCB0aGUgZGF0YSBzdG9yZWQgaW4gdGhlc2UgZmlsZXMgb25jZSB5b3UgaGF2ZSBpdCBpbiBhIGRhdGEgZnJhbWUuIFlvdSBjYW7igJl0IGV2ZW4gY2FsbCBoZWFkKCkgb24gdGhlc2UgY3N2cyEgSG93IGFyZSB5b3Ugc3VwcG9zZWQgdG8gcmVhZCB0aGVtPw0KDQpCZWdpbiBieSBjcmVhdGluZyBhIHZhcmlhYmxlIGNhbGxlZCBmaWxlcyBhbmQgc2V0IGl0IGVxdWFsIHRvIHRoZSBsaXN0LmZpbGVzKCkgb2YgYWxsIG9mIHRoZSBjc3YgZmlsZXMgeW91IHdhbnQgdG8gaW1wb3J0Lg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1UUlVFfQ0KIyBsb2FkIENTVnMNCmZpbGVzIDwtIGxpc3QuZmlsZXMocGF0dGVybiA9ICJzdGF0ZXNfLipjc3YiKQ0KZmlsZXMNCmBgYA0KDQo0LlJlYWQgZWFjaCBmaWxlIGluIGZpbGVzIGludG8gYSBkYXRhIGZyYW1lIHVzaW5nIGxhcHBseSgpIGFuZCBzYXZlIHRoZSByZXN1bHQgdG8gZGZfbGlzdC4NCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgaW5zcGVjdCBkYXRhDQpkZl9saXN0IDwtIGxhcHBseShmaWxlcywgcmVhZC5jc3YpDQpkZl9saXN0DQpgYGANCg0KNS5Db25jYXRlbmF0ZSBhbGwgb2YgdGhlIGRhdGEgZnJhbWVzIGluIGRmX2xpc3QgaW50byBvbmUgZGF0YSBmcmFtZSBjYWxsZWQgdXNfY2Vuc3VzLg0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KIyBpbnNwZWN0IGRhdGENCnVzX2NlbnN1cyA8LSBiaW5kX3Jvd3MoZGZfbGlzdCkNCnVzX2NlbnN1cw0KYGBgDQoNCjYuSW5zcGVjdCB0aGUgdXNfY2Vuc3VzIGRhdGEgZnJhbWUgYnkgcHJpbnRpbmcgdGhlIGNvbHVtbiBuYW1lcywgbG9va2luZyBhdCB0aGUgZGF0YSB0eXBlcyB3aXRoIHN0cigpLCBhbmQgdmlld2luZyB0aGUgaGVhZCgpLg0KDQpXaGF0IGNvbHVtbnMgaGF2ZSBzeW1ib2xzIHRoYXQgd2lsbCBwcmV2ZW50IGNhbGN1bGF0aW9ucz8gV2hhdCBhcmUgdGhlIGRhdGEgdHlwZXMgb2YgdGhlIGNvbHVtbnM/IERvIGFueSBjb2x1bW5zIGNvbnRhaW4gbXVsdGlwbGUga2luZHMgb2YgaW5mb3JtYXRpb24/DQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIGluc3BlY3QgZGF0YQ0KY29sIDwtIGNvbG5hbWVzKHVzX2NlbnN1cykNCmNvbA0Kc3RyKHVzX2NlbnN1cykNCmhlYWQodXNfY2Vuc3VzKQ0KYGBgDQoNCiMgUmVtb3ZlIGFuZCBSZWZvcm1hdCB0aGUgQ29sdW1ucw0KDQo3LldoZW4gaW5zcGVjdGluZyB1c19jZW5zdXMgeW91IG5vdGljZSBhIGNvbHVtbiBYMSB0aGF0IHN0b3JlcyBtZWFuaW5nbGVzcyBpbmZvcm1hdGlvbi4gRHJvcCB0aGUgWDEgY29sdW1uIGZyb20gdXNfY2Vuc3VzLCBhbmQgc2F2ZSB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgdG8gdXNfY2Vuc3VzLiBWaWV3IHRoZSBoZWFkIG9mIHVzX2NlbnN1cy4NCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgZHJvcCBYMSBjb2x1bW4NCnVzX2NlbnN1cyA8LSB1c19jZW5zdXMgJT4lDQogIHNlbGVjdCgtWCkNCnVzX2NlbnN1cw0KYGBgDQoNCjguWW91IG5vdGljZSB0aGF0IHRoZXJlIGFyZSA2IGNvbHVtbnMgcmVwcmVzZW50aW5nIHRoZSBwb3B1bGF0aW9uIHBlcmNlbnRhZ2UgZm9yIGRpZmZlcmVudCByYWNlcy4gVGhlIGNvbHVtbnMgaW5jbHVkZSB0aGUgcGVyY2VudCBzeW1ib2wgJS4gUmVtb3ZlIHRoZSBwZXJjZW50IHN5bWJvbCAlIGZyb20gZWFjaCBvZiB0aGUgcmFjZSBjb2x1bW5zIChIaXNwYW5pYyxXaGl0ZSxCbGFjayxOYXRpdmUsQXNpYW4sUGFjaWZpYykuIFNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIHVzX2NlbnN1cywgYW5kIHZpZXcgdGhlIGhlYWQuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIHJlbW92ZSAlIGZyb20gcmFjZSBjb2x1bW5zDQp1c19jZW5zdXMgPC0gdXNfY2Vuc3VzICU+JQ0KICBtdXRhdGUoSGlzcGFuaWMgPSBnc3ViKCclJywgJycsSGlzcGFuaWMpLCBXaGl0ZSA9IGdzdWIoJyUnLCAnJyxIaXNwYW5pYyksIEJsYWNrID0gZ3N1YignJScsICcnLEhpc3BhbmljKSwgTmF0aXZlID0gZ3N1YignJScsICcnLEhpc3BhbmljKSwgQXNpYW4gPSBnc3ViKCclJywgJycsSGlzcGFuaWMpLCBQYWNpZmljID0gZ3N1YignJScsICcnLEhpc3BhbmljKSkNCg0KaGVhZCh1c19jZW5zdXMpDQpgYGANCg0KOS5UaGUgSW5jb21lIGNvbHVtbiBhbHNvIGluY3VkZXMgYSAkIHN5bWJvbCBhbG9uZyB3aXRoIHRoZSBudW1iZXIgcmVwcmVzZW50aW5nIG1lZGlhbiBpbmNvbWUgZm9yIGEgc3RhdGUuIFJlbW92ZSB0aGUgJCBmcm9tIHRoZSBJbmNvbWUgY29sdW1uLiBTYXZlIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSB0byB1c19jZW5zdXMuIFZpZXcgdGhlIGhlYWQgb2YgdXNfY2Vuc3VzLg0KDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIHJlbW92ZSAkIGZyb20gSW5jb21lIGNvbHVtbg0KI2dzdWIoJyQnLCAnJywgSW5jb21lKSAgIOmAmeWFtuWvpuaYr+OAjOihjOWwvuOAje+8jOS4jeaYr+OAjCQg56ym6Jmf44CNDQojZ3N1YignJCcsICcnLCBJbmNvbWUpIOS4reeahCAnJCcg5piv5q2j5YmH6KGo6YGU5byP5Lit55qE54m55q6K5a2X5YWD77yM5a6D5Luj6KGo44CM6KGM5bC+44CN77yM5LiN5piv5a2X6Z2i5LiK55qEICQg5a2X56ym44CCDQp1c19jZW5zdXMgPC0gdXNfY2Vuc3VzICU+JQ0KICBtdXRhdGUoSW5jb21lID0gZ3N1YignXFwkJywgJycsIEluY29tZSkpDQoNCmhlYWQodXNfY2Vuc3VzKQ0KYGBgDQoNCjEwLlRoZSBHZW5kZXJQb3AgY29sdW1uIGFwcGVhcnMgdG8gaG9sZCB0aGUgbWFsZSBhbmQgZmVtYWxlIHBvcHVsYXRpb24gY291bnRzLiBTZXBhcmF0ZSB0aGlzIGNvbHVtbiBhdCB0aGUgXyBjaGFyYWN0ZXIgdG8gY3JlYXRlIHR3byBuZXcgY29sdW1uczogbWFsZV9wb3AgYW5kIGZlbWFsZV9wb3AuIFNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIHVzX2NlbnN1cywgYW5kIHZpZXcgdGhlIGhlYWQuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIHNlcGFyYXRlIEdlbmRlclBvcCBjb2x1bW4NCnVzX2NlbnN1cyA8LSB1c19jZW5zdXMgJT4lDQogIHNlcGFyYXRlKCdHZW5kZXJQb3AnLCBjKCdtYWxlX3BvcCcsICdmZW1hbGVfcG9wJyksICJfIikNCg0KaGVhZCh1c19jZW5zdXMpDQpgYGANCg0KMTEuWW91IG5vdGljZSB0aGUgbmV3IG1hbGVfcG9wIGFuZCBmZW1hbGVfcG9wIGNvbHVtbnMgY29udGFpbiBleHRyYSBjaGFyYWN0ZXJzIE0gYW5kIEYsIHJlc3BlY3RpdmVseS4gUmVtb3ZlIHRoZXNlIGV4dHJhIGNoYXJhY3RlcnMgZnJvbSB0aGUgY29sdW1ucy4gU2F2ZSB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgdG8gdXNfY2Vuc3VzLCBhbmQgdmlldyB0aGUgaGVhZC4NCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgY2xlYW4gbWFsZSBhbmQgZmVtYWxlIHBvcHVsYXRpb24gY29sdW1ucw0KdXNfY2Vuc3VzIDwtIHVzX2NlbnN1cyAlPiUNCiAgbXV0YXRlKG1hbGVfcG9wID0gZ3N1YignTScsICcnLCBtYWxlX3BvcCksIGZlbWFsZV9wb3AgPSBnc3ViKCdGJywgJycsIGZlbWFsZV9wb3ApKQ0KDQpoZWFkKHVzX2NlbnN1cykNCmBgYA0KDQojIFVwZGF0ZSB0aGUgRGF0YSBUeXBlcw0KDQoxMi5Ob3cgdGhhdCB5b3UgaGF2ZSByZW1vdmVkIGV4dHJhIHN5bWJvbHMgZnJvbSBtYW55IG9mIHRoZSBjb2x1bW5zIHRoYXQgY29udGFpbiBudW1lcmljYWwgZGF0YSwgeW91IG5vdGljZSB0aGF0IHRoZSBkYXRhIHR5cGUgZm9yIHRoZXNlIGNvbHVtbnMgaXMgc3RpbGwgY2hyLCBvciBjaGFyYWN0ZXIuIENvbnZlcnQgYWxsIG9mIHRoZXNlIGNvbHVtbnMgKEhpc3BhbmljLFdoaXRlLEJsYWNrLE5hdGl2ZSxBc2lhbixQYWNpZmljLEluY29tZSxtYWxlX3BvcCxmZW1hbGVfcG9wKSB0byBoYXZlIGEgZGF0YSB0eXBlIG9mIG51bWVyaWMuIFNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIHVzX2NlbnN1cywgYW5kIHZpZXcgdGhlIGhlYWQuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIHVwZGF0ZSBjb2x1bW4gZGF0YSB0eXBlcw0KdXNfY2Vuc3VzIDwtIHVzX2NlbnN1cyAlPiUNCiAgbXV0YXRlKEhpc3BhbmljID0gYXMubnVtZXJpYyhIaXNwYW5pYyksIFdoaXRlID0gYXMubnVtZXJpYyhXaGl0ZSksIEJsYWNrID0gYXMubnVtZXJpYyhCbGFjayksIE5hdGl2ZSA9IGFzLm51bWVyaWMoTmF0aXZlKSwgQXNpYW4gPSBhcy5udW1lcmljKEFzaWFuKSwgUGFjaWZpYyA9IGFzLm51bWVyaWMoUGFjaWZpYyksIEluY29tZSA9IGFzLm51bWVyaWMoSW5jb21lKSwgbWFsZV9wb3AgPSBhcy5udW1lcmljKG1hbGVfcG9wKSwgZmVtYWxlX3BvcCA9IGFzLm51bWVyaWMoZmVtYWxlX3BvcCkpDQpoZWFkKHVzX2NlbnN1cykNCmBgYA0KDQoNCjEzLlRha2UgYSBzZWNvbmQgdG8gbG9vayBiYWNrIGF0IHRoZSBIaXNwYW5pYywgV2hpdGUsIEJsYWNrLCBOYXRpdmUsIEFzaWFuLCBhbmQgUGFjaWZpYyBjb2x1bW5zLiBUaGUgY29sdW1ucyByZXByZXNlbnQgdGhlIHBvcHVsYXRpb24gcGVyY2VudGFnZSBmb3IgZWFjaCByYWNlLiBFYXJsaWVyIHlvdSByZW1vdmVkIHRoZSAlIHN5bWJvbCwgYW5kIHRoZW4geW91IGp1c3QgY29udmVydGVkIHRoZSBjb2x1bW4gdG8gbnVtZXJpYyB0eXBlLiBUbyBtYWtlIGNhbGN1bGF0aW9ucyBlYXNpZXIsIHRoZSBjb2x1bW4gc2hvdWxkIG5vdyByZXByZXNlbnQgcGVyY2VudGFnZXMgaW4gZGVjaW1hbCBmb3JtLCB3aGVyZSA1MCUgaXMgZXF1aXZhbGVudCB0byAwLjUwLiBVcGRhdGUgdGhlIHZhbHVlcyBvZiB0aGVzZSBjb2x1bW5zIHRvIGJlIGluIGRlY2ltYWwgZm9ybSwgYW5kIHNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIHVzX2NlbnN1cy4gVmlldyB0aGUgaGVhZCBvZiB1c19jZW5zdXMuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIHVwZGF0ZSB2YWx1ZXMgb2YgcmFjZSBjb2x1bW5zDQp1c19jZW5zdXMgPC0gdXNfY2Vuc3VzICU+JQ0KICBtdXRhdGUoSGlzcGFuaWMgPSBIaXNwYW5pYyAvIDEwMCwgV2hpdGUgPSBXaGl0ZSAvIDEwMCwgQmxhY2sgPSBCbGFjayAvIDEwMCwgTmF0aXZlID0gTmF0aXZlIC8gMTAwLCBBc2lhbiA9IEFzaWFuIC8gMTAwLCBQYWNpZmljID0gUGFjaWZpYyAvIDEwMCkNCg0KaGVhZCh1c19jZW5zdXMpDQpgYGANCg0KIyBSZW1vdmUgRHVwbGljYXRlIFJvd3MNCg0KMTQuSXTigJlzIGFsd2F5cyBhIGdvb2QgaWRlYSB0byBjaGVjayBpZiB0aGVyZSBhcmUgZHVwbGljYXRlIHJvd3Mgb2YgZGF0YSBpbiBhIGRhdGEgc2V0LiBQaXBlIHVzX2NlbnN1cyBpbnRvIHRoZSBkdXBsaWNhdGVkKCkgZnVuY3Rpb24gdG8gc2VlIHdoaWNoIHJvd3MgYXJlIGR1cGxpY2F0ZWQuIFRoZW4gcGlwZSB0aGUgcmVzdWx0IGludG8gdGFibGUoKSB0byBnZXQgYSBjb3VudCBvZiB0aGUgZHVwbGljYXRlZCByb3dzLg0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KIyBjaGVjayBmb3IgZHVwbGljYXRlIHJvd3MNCmR1cGxpY2F0ZSA8LSB1c19jZW5zdXMgJT4lDQogIGR1cGxpY2F0ZWQoKSAlPiUNCiAgdGFibGUoKQ0KICANCmR1cGxpY2F0ZQ0KYGBgDQoNCjE1LlNpbmNlIHRoZXJlIGFyZSBkdXBsaWNhdGVzLCB1cGRhdGUgdGhlIHZhbHVlIG9mIHVzX2NlbnN1cyB0byBiZSB0aGUgdXNfY2Vuc3VzIGRhdGEgZnJhbWUgd2l0aCBvbmx5IHVuaXF1ZS9kaXN0aW5jdCByb3dzLg0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KIyByZW1vdmUgZHVwbGljYXRlIHJvd3MNCnVzX2NlbnN1cyA8LSB1c19jZW5zdXMgJT4lDQogIGRpc3RpbmN0KCkNCnVzX2NlbnN1cw0KYGBgDQoNCjE2LkNvbmZpcm0gdGhhdCB0aGVyZSBhcmUgbm8gbW9yZSBkdXBsaWNhdGVkIHJvd3MgaW4gdXNfY2Vuc3VzLiBQaXBlIHVzX2NlbnN1cyBpbnRvIHRoZSBkdXBsaWNhdGVkKCkgZnVuY3Rpb24gdG8gc2VlIHdoaWNoIHJvd3MgYXJlIGR1cGxpY2F0ZWQuIFRoZW4gcGlwZSB0aGUgcmVzdWx0IGludG8gdGFibGUoKSB0byBnZXQgYSBjb3VudCBvZiB0aGUgZHVwbGljYXRlZCByb3dzLg0KDQpZb3Ugc2hvdWxkIGV4cGVjdCB0byBzZWUgbm8gVFJVRXMhDQoNCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgY2hlY2sgZm9yIGR1cGxpY2F0ZSByb3dzDQpkdXBsaWNhdGUgPC0gdXNfY2Vuc3VzICU+JQ0KICBkdXBsaWNhdGVkKCkgJT4lDQogIHRhYmxlKCkNCg0KZHVwbGljYXRlDQoNCg0KYGBgDQoNCjE3LlZpZXcgdGhlIGhlYWQoKSBvZiB1c19jZW5zdXMuIFRoZSBkYXRhIGZyYW1lIGlzIGFsbCBjbGVhbiBhbmQgcmVhZHkgZm9yIGFuYWx5c2lzISBXaGF0IGRvIHlvdSB3YW50IHRvIGZpbmQgb3V0Pw0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KIyBjbGVhbiBkYXRhIGZyYW1lDQpoZWFkKHVzX2NlbnN1cykNCmBgYA0KDQo=