Remember, before you can use the tidyverse, you need to load the package.

library(tidyverse)

Back to the Basics

R for statistics

  1. Extract the displ column from the mpg dataset and assign it to the object x
  2. What is the mean and median of x?
  3. What are the largest and smallest values of x? Hint: use either min()/max() or range()
  4. Find the upper and lower quartile of x. What about the inter-quartile range?
  5. What is the variance and standard deviation of x?

Missing Values

  1. What do you think the result of 5 + NA will be? Try it and see if you were correct
  2. Replace + with some other arithmetic operators. Is the result always the same?
  3. Calculate the median of the vector c(2, 6, 3, 4, NA), ignoring missing values
  4. (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

  1. What is the value of 5 + TRUE?
  2. What about 5 + FALSE?
  3. What is the significance of the sum (sum()) of a logical vector?
  4. What about the mean?
  5. Why is it true that FALSE < TRUE? How would this affect ordering by a logical column using arrange()

Comparisons and Boolean Operators

  1. 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

  1. How many automatic cars by Audi are in the mpg dataset?
  2. Who makes the only two cars with a highway mileage above 41 miles/gallon?
  3. Are there any flowers in the iris dataset with petals that are wider than they are long?
  4. What is the carat of the highest priced diamond in the the diamond dataset? (A condition such as price == max(price) may be useful)
  5. 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
  6. 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)
  7. Which observations in the airquality dataset are missing a reading for Ozone

Arrange

  1. Now, using arrange(), find the car in the mpg dataset with the best city mileage. What about the worst?
  2. 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)
  3. 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?
  4. 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

  1. 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
  2. Rename the displ column in mpg to eng_size
  3. Select all columns apart from price in the diamonds dataset
  4. What does the following code do?
select(mpg, 1, 2, 4, 11)

Mutate

  1. 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)
  2. Create column called max_dimension for the diamonds dataset which contains the maximum of the values in the columns x, y, z
  3. Read the help page for transmutate()
  4. The mpg dataset currently has trans stored as a character. Convert this to a factor using mutate() and the factor() function
  5. 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
  6. 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))
  7. 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

  1. What is the variance of the city mileage and highway mileage column in the mpg dataset?
  2. How many cars are there of each class in the mpg dataset? Group by class and then use the n() function to count
  3. What are the median values for each of the 4 numeric columns in the iris dataset?
  4. What is the price of the least expensive diamond of each cut?
  5. What is the mean ozone for each month in the airquality dataset? Make sure you ignore NAs
  6. 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 %>%

  1. 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
  2. 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
  3. Formulate your own question about the diamonds dataset and use a pipeline to answer it

Going Beyond

Ranking

  1. 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
  2. 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
  3. 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

  1. Try running the following code. What error message do you get?
  2. 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))
  1. 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==