Remember, before you can use the tidyverse, you need to load the package.
library(tidyverse)
Back to the Basics
R for statistics
- Extract the
displ
column from the mpg
dataset and assign it to the object x
- What is the mean and median of
x
?
- What are the largest and smallest values of
x
? Hint: use either min()
/max()
or range()
- Find the upper and lower quartile of
x
. What about the inter-quartile range?
- What is the variance and standard deviation of
x
?
Missing Values
- What do you think the result of
5 + NA
will be? Try it and see if you were correct
- Replace
+
with some other arithmetic operators. Is the result always the same?
- Calculate the median of the vector
c(2, 6, 3, 4, NA)
, ignoring missing values
- (From R4DS) Why is
NA ^ 0
not missing? What about NA | TRUE
or NA & FALSE
? Is there a general rule? (NA * 0
is a tricky counterexample - discuss this with me if you are curious)
Arithmetic with Boolean Values
- What is the value of
5 + TRUE
?
- What about
5 + FALSE
?
- What is the significance of the sum (
sum()
) of a logical vector?
- What about the mean?
- Why is it true that
FALSE < TRUE
? How would this affect ordering by a logical column using arrange()
Comparisons and Boolean Operators
- Predict the output of the following R statements. Run them yourself to check your thinking
((4 > 3) & (7 == 6)) | (5 <= 8)
(sqrt(2) ^ 2 == 2) | !near(sqrt(2) ^ 2, 2)
(For this one you may want to look at ?Syntax
)
FALSE & !FALSE | TRUE
The dpylr verbs
Filter
- How many automatic cars by Audi are in the
mpg
dataset?
- Who makes the only two cars with a highway mileage above 41 miles/gallon?
- Are there any flowers in the
iris
dataset with petals that are wider than they are long?
- What is the carat of the highest priced diamond in the the
diamond
dataset? (A condition such as price == max(price)
may be useful)
- For each year in the
mpg
dataset, which car has the smallest engine size? Question 4 may be helpful. Note that you can use group_by()
before filter()
to change the scope of any aggregate functions
- Select all flowers from the
iris
dataset where either the petal length is greater than 6.4 or the petal width is greater than 2.4 (there should be 7 of these)
- Which observations in the
airquality
dataset are missing a reading for Ozone
Arrange
- Now, using
arrange()
, find the car in the mpg
dataset with the best city mileage. What about the worst?
- Order the
iris
dataset by the product of the petal length and width (Note: you don’t need to create a new variable using mutate
to do this)
- Look at the column types when you print
mpg
and diamonds
to the console. What order do you get when you arrange mpg
by class
and what order do you get when you arrange diamonds
by cut
. Why is this?
- Order the cars in
mpg
by the difference between their city and highway mileage (biggest difference first) - if you wanted to do this properly you should use the abs()
function, or you can use a filter to check that there are no cars with city mileage higher than highway mileage
Select
- Re-order the columns of the
iris
dataset so that Species is first. To do this, select Species
first and then use the everything()
helper to select everything else
- Rename the
displ
column in mpg
to eng_size
- Select all columns apart from
price
in the diamonds dataset
- What does the following code do?
select(mpg, 1, 2, 4, 11)
Mutate
- Using the
mpg
dataset, create a new column called cty_km_l
which measures the city mileage in kilometres per litre (Hint: 1 mile ~ 1.6 km, 1 gallon ~ 3.8 litres)
- Create column called
max_dimension
for the diamonds
dataset which contains the maximum of the values in the columns x
, y
, z
- Read the help page for
transmutate()
- The
mpg
dataset currently has trans
stored as a character. Convert this to a factor using mutate()
and the factor()
function
- Create a column called
is_automatic
which is TRUE
if and only if a given car has automatic transmission. Don’t forget to use ==
for comparison
mutate
can be combined with group_by
to change the scope of aggregate functions. Use this to create a new variable in the mpg
dataset called best_in_class
which is TRUE
if and only if the highway mileage is the highest out of all other cars in that class. (Hint: first group by class then use hwy == max(hwy)
)
- Create a new column in the
diamonds
dataset called expensive
which is TRUE
if and only if the price of the diamond is above the upper quartile of all prices
Summarise
- What is the variance of the city mileage and highway mileage column in the
mpg
dataset?
- How many cars are there of each class in the
mpg
dataset? Group by class
and then use the n()
function to count
- What are the median values for each of the 4 numeric columns in the
iris
dataset?
- What is the price of the least expensive diamond of each cut?
- What is the mean ozone for each month in the
airquality
dataset? Make sure you ignore NA
s
- For each cut and colour in the diamonds dataset, what is the range (
diff(range(...))
) of the carats?
Pipelines
In this section, all code should be written by piping the output of each function into the next using %>%
- After removing all 2-seater cars, calculate the average of the city and highway mileage for each car and add this as a new column called
efficiency.
Group by manufacturer and the calculate the maximum efficiency for each group. Arrange these in descending order of efficiency
- Create a new column in the
iris
dataset called Petal.Area
which is the product of the petal length and width. Create a similar column called Sepal.Area
. Pipe this data frame into a call to ggplot
to create a plot of these two variables, coloured by Species. Add a line of best fit for each species
- Formulate your own question about the
diamonds
dataset and use a pipeline to answer it
Going Beyond
Ranking
- Read the help page for
ranking
. In particular try to understand the difference between row_number()
, min_rank()
, dense_rank()
. The others are of less use
- For each date in the
airquality
dataset, rank observations (think about which ranking method is most appropriate) by the intensity of solar radiation compared to other observations in the same month. Select only observations which have the 2nd highest solar radiation for each month
- For each year in the
mpg
dataset, rank the cars by city mileage so that the lowest mileage is ranked first. Select the cars that are ranked number one for each year
Un-grouping
- Try running the following code. What error message do you get?
- Run the code line by line, where do things go wrong?
as_tibble(Titanic) %>%
group_by(Class, Age) %>%
summarise(count = sum(n)) %>%
mutate(Class = reorder(Class, count))
- Read the help page for
ungroup
. How might you rectify this issue?
LS0tCnRpdGxlOiAiSW50byB0aGUgVGlkeXZlcnNlIgpzdWJ0aXRsZTogIlNlc3Npb24gVGhyZWUgRXhlcmNpc2VzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpSZW1lbWJlciwgYmVmb3JlIHlvdSBjYW4gdXNlIHRoZSB0aWR5dmVyc2UsIHlvdSBuZWVkIHRvIGxvYWQgdGhlIHBhY2thZ2UuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyBCYWNrIHRvIHRoZSBCYXNpY3MKCiMjIyMgUiBmb3Igc3RhdGlzdGljcwoKMS4gRXh0cmFjdCB0aGUgYGRpc3BsYCBjb2x1bW4gZnJvbSB0aGUgYG1wZ2AgZGF0YXNldCBhbmQgYXNzaWduIGl0IHRvIHRoZSBvYmplY3QgYHhgCjIuIFdoYXQgaXMgdGhlIG1lYW4gYW5kIG1lZGlhbiBvZiBgeGA/CjMuIFdoYXQgYXJlIHRoZSBsYXJnZXN0IGFuZCBzbWFsbGVzdCB2YWx1ZXMgb2YgYHhgPyBIaW50OiB1c2UgZWl0aGVyIGBtaW4oKWAvYG1heCgpYCBvciBgcmFuZ2UoKWAKNC4gRmluZCB0aGUgdXBwZXIgYW5kIGxvd2VyIHF1YXJ0aWxlIG9mIGB4YC4gV2hhdCBhYm91dCB0aGUgaW50ZXItcXVhcnRpbGUgcmFuZ2U/CjUuIFdoYXQgaXMgdGhlIHZhcmlhbmNlIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYHhgPwoKIyMjIyBNaXNzaW5nIFZhbHVlcwoKMS4gV2hhdCBkbyB5b3UgdGhpbmsgdGhlIHJlc3VsdCBvZiBgNSArIE5BYCB3aWxsIGJlPyBUcnkgaXQgYW5kIHNlZSBpZiB5b3Ugd2VyZSBjb3JyZWN0CjIuIFJlcGxhY2UgYCtgIHdpdGggc29tZSBvdGhlciBhcml0aG1ldGljIG9wZXJhdG9ycy4gSXMgdGhlIHJlc3VsdCBhbHdheXMgdGhlIHNhbWU/CjMuIENhbGN1bGF0ZSB0aGUgbWVkaWFuIG9mIHRoZSB2ZWN0b3IgYGMoMiwgNiwgMywgNCwgTkEpYCwgaWdub3JpbmcgbWlzc2luZyB2YWx1ZXMKNC4gKEZyb20gUjREUykgV2h5IGlzIGBOQSBeIDBgIG5vdCBtaXNzaW5nPyBXaGF0IGFib3V0IGBOQSB8IFRSVUVgIG9yIGBOQSAmIEZBTFNFYD8gSXMgdGhlcmUgYSBnZW5lcmFsIHJ1bGU/IChgTkEgKiAwYCBpcyBhIHRyaWNreSBjb3VudGVyZXhhbXBsZSAtIGRpc2N1c3MgdGhpcyB3aXRoIG1lIGlmIHlvdSBhcmUgY3VyaW91cykKCiMjIyMgQXJpdGhtZXRpYyB3aXRoIEJvb2xlYW4gVmFsdWVzCgoxLiBXaGF0IGlzIHRoZSB2YWx1ZSBvZiBgNSArIFRSVUVgPwoyLiBXaGF0IGFib3V0IGA1ICsgRkFMU0VgPwozLiBXaGF0IGlzIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIHN1bSAoYHN1bSgpYCkgb2YgYSBsb2dpY2FsIHZlY3Rvcj8KNC4gV2hhdCBhYm91dCB0aGUgbWVhbj8KNS4gV2h5IGlzIGl0IHRydWUgdGhhdCBgRkFMU0UgPCBUUlVFYD8gSG93IHdvdWxkIHRoaXMgYWZmZWN0IG9yZGVyaW5nIGJ5IGEgbG9naWNhbCBjb2x1bW4gdXNpbmcgYGFycmFuZ2UoKWAKCiMjIyMgQ29tcGFyaXNvbnMgYW5kIEJvb2xlYW4gT3BlcmF0b3JzCgoxLiBQcmVkaWN0IHRoZSBvdXRwdXQgb2YgdGhlIGZvbGxvd2luZyBSIHN0YXRlbWVudHMuIFJ1biB0aGVtIHlvdXJzZWxmIHRvIGNoZWNrIHlvdXIgdGhpbmtpbmcKCmBgYHtyIGV2YWw9RkFMU0V9CigoNCA+IDMpICYgKDcgPT0gNikpIHwgKDUgPD0gOCkKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQooc3FydCgyKSBeIDIgPT0gMikgfCAhbmVhcihzcXJ0KDIpIF4gMiwgMikKYGBgCgooRm9yIHRoaXMgb25lIHlvdSBtYXkgd2FudCB0byBsb29rIGF0IGA/U3ludGF4YCkKCmBgYHtyIGV2YWw9RkFMU0V9CkZBTFNFICYgIUZBTFNFIHwgVFJVRQpgYGAKCiMjIFRoZSBkcHlsciB2ZXJicwoKIyMjIEZpbHRlcgoKMS4gSG93IG1hbnkgYXV0b21hdGljIGNhcnMgYnkgQXVkaSBhcmUgaW4gdGhlIGBtcGdgIGRhdGFzZXQ/CjIuIFdobyBtYWtlcyB0aGUgb25seSB0d28gY2FycyB3aXRoIGEgaGlnaHdheSBtaWxlYWdlIGFib3ZlIDQxIG1pbGVzL2dhbGxvbj8KMy4gQXJlIHRoZXJlIGFueSBmbG93ZXJzIGluIHRoZSBgaXJpc2AgZGF0YXNldCB3aXRoIHBldGFscyB0aGF0IGFyZSB3aWRlciB0aGFuIHRoZXkgYXJlIGxvbmc/CjQuIFdoYXQgaXMgdGhlIGNhcmF0IG9mIHRoZSBoaWdoZXN0IHByaWNlZCBkaWFtb25kIGluIHRoZSB0aGUgYGRpYW1vbmRgIGRhdGFzZXQ/IChBIGNvbmRpdGlvbiBzdWNoIGFzIGBwcmljZSA9PSBtYXgocHJpY2UpYCBtYXkgYmUgdXNlZnVsKQo1LiBGb3IgZWFjaCB5ZWFyIGluIHRoZSBgbXBnYCBkYXRhc2V0LCB3aGljaCBjYXIgaGFzIHRoZSBzbWFsbGVzdCBlbmdpbmUgc2l6ZT8gUXVlc3Rpb24gNCBtYXkgYmUgaGVscGZ1bC4gTm90ZSB0aGF0IHlvdSBjYW4gdXNlIGBncm91cF9ieSgpYCBiZWZvcmUgYGZpbHRlcigpYCB0byBjaGFuZ2UgdGhlIHNjb3BlIG9mIGFueSBhZ2dyZWdhdGUgZnVuY3Rpb25zCjYuIFNlbGVjdCBhbGwgZmxvd2VycyBmcm9tIHRoZSBgaXJpc2AgZGF0YXNldCB3aGVyZSBlaXRoZXIgdGhlIHBldGFsIGxlbmd0aCBpcyBncmVhdGVyIHRoYW4gNi40IG9yIHRoZSBwZXRhbCB3aWR0aCBpcyBncmVhdGVyIHRoYW4gMi40ICh0aGVyZSBzaG91bGQgYmUgNyBvZiB0aGVzZSkKNy4gV2hpY2ggb2JzZXJ2YXRpb25zIGluIHRoZSBgYWlycXVhbGl0eWAgZGF0YXNldCBhcmUgbWlzc2luZyBhIHJlYWRpbmcgZm9yIGBPem9uZWAKCiMjIyBBcnJhbmdlCgoxLiBOb3csIHVzaW5nIGBhcnJhbmdlKClgLCBmaW5kIHRoZSBjYXIgaW4gdGhlIGBtcGdgIGRhdGFzZXQgd2l0aCB0aGUgYmVzdCBjaXR5IG1pbGVhZ2UuIFdoYXQgYWJvdXQgdGhlIHdvcnN0PwoyLiBPcmRlciB0aGUgYGlyaXNgIGRhdGFzZXQgYnkgdGhlIHByb2R1Y3Qgb2YgdGhlIHBldGFsIGxlbmd0aCBhbmQgd2lkdGggKE5vdGU6IHlvdSBkb24ndCBuZWVkIHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSB1c2luZyBgbXV0YXRlYCB0byBkbyB0aGlzKQozLiBMb29rIGF0IHRoZSBjb2x1bW4gdHlwZXMgd2hlbiB5b3UgcHJpbnQgYG1wZ2AgYW5kIGBkaWFtb25kc2AgdG8gdGhlIGNvbnNvbGUuIFdoYXQgb3JkZXIgZG8geW91IGdldCB3aGVuIHlvdSBhcnJhbmdlIGBtcGdgIGJ5IGBjbGFzc2AgYW5kIHdoYXQgb3JkZXIgZG8geW91IGdldCB3aGVuIHlvdSBhcnJhbmdlIGBkaWFtb25kc2AgYnkgYGN1dGAuIFdoeSBpcyB0aGlzPwo0LiBPcmRlciB0aGUgY2FycyBpbiBgbXBnYCBieSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZWlyIGNpdHkgYW5kIGhpZ2h3YXkgbWlsZWFnZSAoYmlnZ2VzdCBkaWZmZXJlbmNlIGZpcnN0KSAtIGlmIHlvdSB3YW50ZWQgdG8gZG8gdGhpcyBwcm9wZXJseSB5b3Ugc2hvdWxkIHVzZSB0aGUgYGFicygpYCBmdW5jdGlvbiwgb3IgeW91IGNhbiB1c2UgYSBmaWx0ZXIgdG8gY2hlY2sgdGhhdCB0aGVyZSBhcmUgbm8gY2FycyB3aXRoIGNpdHkgbWlsZWFnZSBoaWdoZXIgdGhhbiBoaWdod2F5IG1pbGVhZ2UKCiMjIyBTZWxlY3QKCjEuIFJlLW9yZGVyIHRoZSBjb2x1bW5zIG9mIHRoZSBgaXJpc2AgZGF0YXNldCBzbyB0aGF0IFNwZWNpZXMgaXMgZmlyc3QuIFRvIGRvIHRoaXMsIHNlbGVjdCBgU3BlY2llc2AgZmlyc3QgYW5kIHRoZW4gdXNlIHRoZSBgZXZlcnl0aGluZygpYCBoZWxwZXIgdG8gc2VsZWN0IGV2ZXJ5dGhpbmcgZWxzZQoyLiBSZW5hbWUgdGhlIGBkaXNwbGAgY29sdW1uIGluIGBtcGdgIHRvIGBlbmdfc2l6ZWAKMy4gU2VsZWN0IGFsbCBjb2x1bW5zIGFwYXJ0IGZyb20gYHByaWNlYCBpbiB0aGUgZGlhbW9uZHMgZGF0YXNldAo0LiBXaGF0IGRvZXMgdGhlIGZvbGxvd2luZyBjb2RlIGRvPyAKCmBgYHtyIGV2YWw9RkFMU0V9CnNlbGVjdChtcGcsIDEsIDIsIDQsIDExKQpgYGAKCiMjIyBNdXRhdGUKCjEuIFVzaW5nIHRoZSBgbXBnYCBkYXRhc2V0LCBjcmVhdGUgYSBuZXcgY29sdW1uIGNhbGxlZCBgY3R5X2ttX2xgIHdoaWNoIG1lYXN1cmVzIHRoZSBjaXR5IG1pbGVhZ2UgaW4ga2lsb21ldHJlcyBwZXIgbGl0cmUgKEhpbnQ6IDEgbWlsZSB+IDEuNiBrbSwgMSBnYWxsb24gfiAzLjggbGl0cmVzKQoyLiBDcmVhdGUgY29sdW1uIGNhbGxlZCBgbWF4X2RpbWVuc2lvbmAgZm9yIHRoZSBgZGlhbW9uZHNgIGRhdGFzZXQgd2hpY2ggY29udGFpbnMgdGhlIG1heGltdW0gb2YgdGhlIHZhbHVlcyBpbiB0aGUgY29sdW1ucyBgeGAsIGB5YCwgYHpgCjMuIFJlYWQgdGhlIGhlbHAgcGFnZSBmb3IgYHRyYW5zbXV0YXRlKClgCjQuIFRoZSBgbXBnYCBkYXRhc2V0IGN1cnJlbnRseSBoYXMgYHRyYW5zYCBzdG9yZWQgYXMgYSBjaGFyYWN0ZXIuIENvbnZlcnQgdGhpcyB0byBhIGZhY3RvciB1c2luZyBgbXV0YXRlKClgIGFuZCB0aGUgYGZhY3RvcigpYCBmdW5jdGlvbgo1LiBDcmVhdGUgYSBjb2x1bW4gY2FsbGVkIGBpc19hdXRvbWF0aWNgIHdoaWNoIGlzIGBUUlVFYCBpZiBhbmQgb25seSBpZiBhIGdpdmVuIGNhciBoYXMgYXV0b21hdGljIHRyYW5zbWlzc2lvbi4gRG9uJ3QgZm9yZ2V0IHRvIHVzZSBgPT1gIGZvciBjb21wYXJpc29uCjYuIGBtdXRhdGVgIGNhbiBiZSBjb21iaW5lZCB3aXRoIGBncm91cF9ieWAgdG8gY2hhbmdlIHRoZSBzY29wZSBvZiBhZ2dyZWdhdGUgZnVuY3Rpb25zLiBVc2UgdGhpcyB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgaW4gdGhlIGBtcGdgIGRhdGFzZXQgY2FsbGVkIGBiZXN0X2luX2NsYXNzYCB3aGljaCBpcyBgVFJVRWAgaWYgYW5kIG9ubHkgaWYgdGhlIGhpZ2h3YXkgbWlsZWFnZSBpcyB0aGUgaGlnaGVzdCBvdXQgb2YgYWxsIG90aGVyIGNhcnMgaW4gdGhhdCBjbGFzcy4gKEhpbnQ6IGZpcnN0IGdyb3VwIGJ5IGNsYXNzIHRoZW4gdXNlIGBod3kgPT0gbWF4KGh3eSlgKQo3LiBDcmVhdGUgYSBuZXcgY29sdW1uIGluIHRoZSBgZGlhbW9uZHNgIGRhdGFzZXQgY2FsbGVkIGBleHBlbnNpdmVgIHdoaWNoIGlzIGBUUlVFYCBpZiBhbmQgb25seSBpZiB0aGUgcHJpY2Ugb2YgdGhlIGRpYW1vbmQgaXMgYWJvdmUgdGhlIHVwcGVyIHF1YXJ0aWxlIG9mIGFsbCBwcmljZXMKCiMjIyBTdW1tYXJpc2UKCjEuIFdoYXQgaXMgdGhlIHZhcmlhbmNlIG9mIHRoZSBjaXR5IG1pbGVhZ2UgYW5kIGhpZ2h3YXkgbWlsZWFnZSBjb2x1bW4gaW4gdGhlIGBtcGdgIGRhdGFzZXQ/CjIuIEhvdyBtYW55IGNhcnMgYXJlIHRoZXJlIG9mIGVhY2ggY2xhc3MgaW4gdGhlIGBtcGdgIGRhdGFzZXQ/IEdyb3VwIGJ5IGBjbGFzc2AgYW5kIHRoZW4gdXNlIHRoZSBgbigpYCBmdW5jdGlvbiB0byBjb3VudAozLiBXaGF0IGFyZSB0aGUgbWVkaWFuIHZhbHVlcyBmb3IgZWFjaCBvZiB0aGUgNCBudW1lcmljIGNvbHVtbnMgaW4gdGhlIGBpcmlzYCBkYXRhc2V0Pwo0LiBXaGF0IGlzIHRoZSBwcmljZSBvZiB0aGUgbGVhc3QgZXhwZW5zaXZlIGRpYW1vbmQgb2YgZWFjaCBjdXQ/CjUuIFdoYXQgaXMgdGhlIG1lYW4gb3pvbmUgZm9yIGVhY2ggbW9udGggaW4gdGhlIGBhaXJxdWFsaXR5YCBkYXRhc2V0PyBNYWtlIHN1cmUgeW91IGlnbm9yZSBgTkFgcwo2LiBGb3IgZWFjaCBjdXQgYW5kIGNvbG91ciBpbiB0aGUgZGlhbW9uZHMgZGF0YXNldCwgd2hhdCBpcyB0aGUgcmFuZ2UgKGBkaWZmKHJhbmdlKC4uLikpYCkgb2YgdGhlIGNhcmF0cz8KCiMjIyBQaXBlbGluZXMKCkluIHRoaXMgc2VjdGlvbiwgYWxsIGNvZGUgc2hvdWxkIGJlIHdyaXR0ZW4gYnkgcGlwaW5nIHRoZSBvdXRwdXQgb2YgZWFjaCBmdW5jdGlvbiBpbnRvIHRoZSBuZXh0IHVzaW5nIGAlPiVgCgoxLiBBZnRlciByZW1vdmluZyBhbGwgMi1zZWF0ZXIgY2FycywgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIHRoZSBjaXR5IGFuZCBoaWdod2F5IG1pbGVhZ2UgZm9yIGVhY2ggY2FyIGFuZCBhZGQgdGhpcyBhcyBhIG5ldyBjb2x1bW4gY2FsbGVkIGBlZmZpY2llbmN5LmAgR3JvdXAgYnkgbWFudWZhY3R1cmVyIGFuZCB0aGUgY2FsY3VsYXRlIHRoZSBtYXhpbXVtIGVmZmljaWVuY3kgZm9yIGVhY2ggZ3JvdXAuIEFycmFuZ2UgdGhlc2UgaW4gZGVzY2VuZGluZyBvcmRlciBvZiBlZmZpY2llbmN5CjIuIENyZWF0ZSBhIG5ldyBjb2x1bW4gaW4gdGhlIGBpcmlzYCBkYXRhc2V0IGNhbGxlZCBgUGV0YWwuQXJlYWAgd2hpY2ggaXMgdGhlIHByb2R1Y3Qgb2YgdGhlIHBldGFsIGxlbmd0aCBhbmQgd2lkdGguIENyZWF0ZSBhIHNpbWlsYXIgY29sdW1uIGNhbGxlZCBgU2VwYWwuQXJlYWAuIFBpcGUgdGhpcyBkYXRhIGZyYW1lIGludG8gYSBjYWxsIHRvIGBnZ3Bsb3RgIHRvIGNyZWF0ZSBhIHBsb3Qgb2YgdGhlc2UgdHdvIHZhcmlhYmxlcywgY29sb3VyZWQgYnkgU3BlY2llcy4gQWRkIGEgbGluZSBvZiBiZXN0IGZpdCBmb3IgZWFjaCBzcGVjaWVzCjMuIEZvcm11bGF0ZSB5b3VyIG93biBxdWVzdGlvbiBhYm91dCB0aGUgYGRpYW1vbmRzYCBkYXRhc2V0IGFuZCB1c2UgYSBwaXBlbGluZSB0byBhbnN3ZXIgaXQKCiMjIEdvaW5nIEJleW9uZAoKIyMjIFJhbmtpbmcKCjEuIFJlYWQgdGhlIGhlbHAgcGFnZSBmb3IgYHJhbmtpbmdgLiBJbiBwYXJ0aWN1bGFyIHRyeSB0byB1bmRlcnN0YW5kIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYHJvd19udW1iZXIoKWAsIGBtaW5fcmFuaygpYCwgYGRlbnNlX3JhbmsoKWAuIFRoZSBvdGhlcnMgYXJlIG9mIGxlc3MgdXNlCjIuIEZvciBlYWNoIGRhdGUgaW4gdGhlIGBhaXJxdWFsaXR5YCBkYXRhc2V0LCByYW5rIG9ic2VydmF0aW9ucyAodGhpbmsgYWJvdXQgd2hpY2ggcmFua2luZyBtZXRob2QgaXMgbW9zdCBhcHByb3ByaWF0ZSkgYnkgdGhlIGludGVuc2l0eSBvZiBzb2xhciByYWRpYXRpb24gY29tcGFyZWQgdG8gb3RoZXIgb2JzZXJ2YXRpb25zIF9pbiB0aGUgc2FtZSBtb250aF8uIFNlbGVjdCBvbmx5IG9ic2VydmF0aW9ucyB3aGljaCBoYXZlIHRoZSAybmQgaGlnaGVzdCBzb2xhciByYWRpYXRpb24gZm9yIGVhY2ggbW9udGgKMy4gRm9yIGVhY2ggeWVhciBpbiB0aGUgYG1wZ2AgZGF0YXNldCwgcmFuayB0aGUgY2FycyBieSBjaXR5IG1pbGVhZ2Ugc28gdGhhdCB0aGUgbG93ZXN0IG1pbGVhZ2UgaXMgcmFua2VkIGZpcnN0LiBTZWxlY3QgdGhlIGNhcnMgdGhhdCBhcmUgcmFua2VkIG51bWJlciBvbmUgZm9yIGVhY2ggeWVhcgoKIyMjIFVuLWdyb3VwaW5nCgoxLiBUcnkgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvZGUuIFdoYXQgZXJyb3IgbWVzc2FnZSBkbyB5b3UgZ2V0PwoyLiBSdW4gdGhlIGNvZGUgbGluZSBieSBsaW5lLCB3aGVyZSBkbyB0aGluZ3MgZ28gd3Jvbmc/CgpgYGB7ciBldmFsPUZBTFNFfQphc190aWJibGUoVGl0YW5pYykgJT4lIAogICAgIGdyb3VwX2J5KENsYXNzLCBBZ2UpICU+JSAKICAgICBzdW1tYXJpc2UoY291bnQgPSBzdW0obikpICU+JSAKICAgICBtdXRhdGUoQ2xhc3MgPSByZW9yZGVyKENsYXNzLCBjb3VudCkpCmBgYAoKMy4gUmVhZCB0aGUgaGVscCBwYWdlIGZvciBgdW5ncm91cGAuIEhvdyBtaWdodCB5b3UgcmVjdGlmeSB0aGlzIGlzc3VlPw==