Question 1. Wrong file extension

As the instructor mentioned in class, the file extensions does not dictate the contents of a file. We receive student grade file which is supposed to be a csv file. We are interested in students with letter_grade “A”. But our filtering does not work. Please fix the code below:

download.file("https://s3-us-west-2.amazonaws.com/veri-analizi/student_grades.csv","student_grades.csv")
grades <- read_csv("student_grades.csv")

grades %>% 
  filter(letter_grade=="A")

The file is located at: https://s3-us-west-2.amazonaws.com/veri-analizi/student_grades.csv

Solution 1

The file is not a csv file, thus we should not be using read.csv or read_csv functions. As you remember from the discussion in class, there are custom functions for specific file types (csv, tsv) and then there’s generic functions read.table and read_delim. The delimeter is | character and there are two comment line on top. As you can see, both functions have completely different options.

library(dplyr)
# this is base R solution and it automatically skips first 2 commnents ilne, due to `comment.char = "#"` default option
grades <- read.table("student_grades.csv", sep = "|", header = TRUE, stringsAsFactors = FALSE)  
                                                                                                
grades %>% 
  filter(letter_grade=="A")
# OR
library(readr)
grades2 <- read_delim("student_grades.csv",  delim = "|", skip=2)  #this is tidyverse solution
grades2 %>% 
  filter(letter_grade=="A")

Below is the related slide from “Importing Data in R” course, Chapter 2.

Import with delim or sep options

Import with delim or sep options

Question 2. Genes and phenotypes

A. Genes without phenotype

Using human_genes and phenotypes tables (which are loaded for you), please calculate the following: The number of genes which does not have any phenotype associatied with them. (please use join)

Solution 2.A

# gene_id column is the key
# let's use anti_join first
anti_join(human_genes,phenotypes) %>% 
  count()
Joining, by = "gene_id"
# OR we can use left_join
left_join(human_genes, phenotypes) %>% 
  filter(is.na(phenotype)) %>% 
#  summarise(count=n_distinct(gene_id))
  count()
Joining, by = "gene_id"

B. Bad chromosomes

Using human_genes and phenotypes tables, please calculate how many genes each chromosome carry which are associated with a phenotype. Please do not count a gene multiple time if it’s associated with multiple phenotypes.

Expected output:

Chromosome count
.. ..

Solution 2.B

inner_join(human_genes,phenotypes) %>% 
  group_by(chr) %>% 
  summarise(count=n_distinct(gene_id))
Joining, by = "gene_id"

Question 3. A Bad Day of Month

Figure: Total and Delayed number of flights for days of month with highest percentage

Figure: Total and Delayed number of flights for days of month with highest percentage

The plot describes top 10 days of month which have highest percentage of delays (percentage= number of delayed flights on that Day of Month / total number of flights on that Day of Month). You can consider a flight as “delayed” if Departure delay is equal to or greater than 5 minutes. Blue dots show total flights and red dots show delayed flight counts. Please write down the code below which can produce the plot above.

Solution 3

library(ggplot2)
hflights %>% 
  group_by(DayofMonth) %>% 
  summarise(n_flights=n(),
            n_delays=sum(DepDelay > 5, na.rm=TRUE),  # counting delays via logical vector
            p_delay=n_delays/n_flights*100) %>%      # percentage calculation
  filter(rank(-p_delay) < 11 ) %>%    # extracting top 10 with rank(), minus means 
                                      # according to reverse order, highest p_delay value
#  arrange(-p_delay) %>% 
  ggplot(aes(x=DayofMonth)) +
  geom_point(aes(y=n_flights),color="blue") +
  geom_point(aes(y=n_delays),color="red") +
  ylab("Number of \ndelayed or total flights")  # this labeling was not required

By the way, DayOfMonth should not be treated as number in this graph, it should be treated as factor. If you add mutate(DayofMonth=as.factor(DayofMonth)) just before group_by line, then you get the following plot, which is more accurate.

Here’s the screenshot from DataCamp exercise which uses percentage calculation and counting with sum() over logical vector technique. “Data Manipulation in R with dplyr” lecture, Chapter 5, Exercise title “Unite and conquer using group_by”

dplyr percentage and sum

dplyr percentage and sum

Question 4. A Bad Day for Everyone

You already studied and are aware of hflights table from DataCamp courses. Two more tables are already defined for you (when you run the first chunk). Instead of using lookup tables as you did DataCamp, let’s use join to combine all data. Use airline_codes and airport_codes tables (already loaded for you) to find out the worst day for each airline. The worst day is defined as the maximum departure delay, so please find out max departure delay of each airline and then join with necessary tables to get the output below:

Expected output:

Airline Destination Max Dep Delay
.. .. ..

“Airline” column should have carrier’s full name and “Destination” column should have full name of airport.

Hint: please consider using rank() function

Solution 4

hflights %>% 
  inner_join(airline_codes, by=c("UniqueCarrier"="name")) %>%   # the keys have different names
  inner_join(airport_codes, by=c("Dest"="faa")) %>%             # the keys have different names
  group_by(UniqueCarrier) %>%        # for using rank() within airline groups, we need group_by
  filter(rank(-DepDelay)==1) %>%     # select lines whose rank is 1 in reverse (highest DepDelay)
  ungroup() %>%                      # this is not required but generates cleaner result
  select(Airline=value,Destination=name,Max_Dep_Delay=DepDelay)  # select and rename at the same time

Here’s the screenshot from DataCamp exercise which uses mutate(rank=rank(desc(n))) %>% filter(rank==1) to pick rows with highest rank. In our solution we used filter(rank(-DepDelay)==1) to skip mutate() step. “Data Manipulation in R with dplyr” lecture, Chapter 5, Exercise title “Advanced group_by exercises”

dplyr rank and filter

dplyr rank and filter

LS0tCnRpdGxlOiAiR3IxIE1pZHRlcm0gU29sdXRpb25zIgphdXRob3I6ICJhbHBlciB5aWxtYXoiCmRhdGU6ICJOb3ZlbWJlciAxNSwgMjAxNyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgdGhlcmUgYXJlIG1pc3NpbmcvbmVjZXNzYXJ5IGxpYnJhcmllcyBwbGVhc2UgbG9hZCB0aGVtIHlvdXJzZWxmCmxpYnJhcnkoaGZsaWdodHMpCgpmaWxlc190b19kb3dubG9hZCA8LSBjKCJhaXJsaW5lX2NvZGVzIiwicGhlbm90eXBlcyIsImFpcnBvcnRfY29kZXMiLCJodW1hbl9nZW5lcyIpCnVybCA8LSAiaHR0cHM6Ly9zMy11cy13ZXN0LTIuYW1hem9uYXdzLmNvbS92ZXJpLWFuYWxpemkvIgojIHdlIHNob3VsZCBub3QgYmUgZG93bmxvYWRpbmcgZmlsZXMgYXQgZWFjaCBydW4gLSBmb3Igc2luZ2xlIHJ1biB0aGlzIGlzIGJldHRlcjogcmVhZFJEUyhnemNvbih1cmwoInJlbW90ZS11cmwtcmRzLWZpbGUiKSkpCmRvd25sb2FkcyA8LSBsYXBwbHkoZmlsZXNfdG9fZG93bmxvYWQsIGZ1bmN0aW9uKHgpIHsKICBpZiAoIWZpbGUuZXhpc3RzKHBhc3RlMCh4ICwgIi5yZHMiKSkpIHsgZG93bmxvYWQuZmlsZShwYXN0ZTAoIHVybCAsIHggLCAiLnJkcyIgKSwgcGFzdGUwKHggLCAiLnJkcyIpKQogIH0gIAogIGFzc2lnbiggeCwgcmVhZFJEUyggcGFzdGUwKCB4ICwgIi5yZHMiICkpLCBlbnZpciA9IHBhcmVudC5mcmFtZSgyKSApIApOVUxMCn0pCmBgYAoKIyBRdWVzdGlvbiAxLiBXcm9uZyBmaWxlIGV4dGVuc2lvbgoKQXMgdGhlIGluc3RydWN0b3IgbWVudGlvbmVkIGluIGNsYXNzLCB0aGUgZmlsZSBleHRlbnNpb25zIGRvZXMgbm90IGRpY3RhdGUgdGhlIGNvbnRlbnRzIG9mIGEgZmlsZS4gV2UgcmVjZWl2ZSBzdHVkZW50IGdyYWRlIGZpbGUgd2hpY2ggaXMgc3VwcG9zZWQgdG8gYmUgYSBjc3YgZmlsZS4gV2UgYXJlIGludGVyZXN0ZWQgaW4gc3R1ZGVudHMgd2l0aCBsZXR0ZXJfZ3JhZGUgIkEiLiBCdXQgb3VyIGZpbHRlcmluZyBkb2VzIG5vdCB3b3JrLiBQbGVhc2UgZml4IHRoZSBjb2RlIGJlbG93OgoKYGBge3IgYW5zd2VyMSwgZXZhbD1GQUxTRX0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9zMy11cy13ZXN0LTIuYW1hem9uYXdzLmNvbS92ZXJpLWFuYWxpemkvc3R1ZGVudF9ncmFkZXMuY3N2Iiwic3R1ZGVudF9ncmFkZXMuY3N2IikKZ3JhZGVzIDwtIHJlYWRfY3N2KCJzdHVkZW50X2dyYWRlcy5jc3YiKQoKZ3JhZGVzICU+JSAKICBmaWx0ZXIobGV0dGVyX2dyYWRlPT0iQSIpCmBgYAoKVGhlIGZpbGUgaXMgbG9jYXRlZCBhdDogaHR0cHM6Ly9zMy11cy13ZXN0LTIuYW1hem9uYXdzLmNvbS92ZXJpLWFuYWxpemkvc3R1ZGVudF9ncmFkZXMuY3N2CgojIFNvbHV0aW9uIDEKClRoZSBmaWxlIGlzIG5vdCBhIGNzdiBmaWxlLCB0aHVzIHdlIHNob3VsZCBub3QgYmUgdXNpbmcgYHJlYWQuY3N2YCBvciBgcmVhZF9jc3ZgIGZ1bmN0aW9ucy4gQXMgeW91IHJlbWVtYmVyIGZyb20gdGhlIGRpc2N1c3Npb24gaW4gY2xhc3MsIHRoZXJlIGFyZSBjdXN0b20gZnVuY3Rpb25zIGZvciBzcGVjaWZpYyBmaWxlIHR5cGVzIChjc3YsIHRzdikgYW5kIHRoZW4gdGhlcmUncyBnZW5lcmljIGZ1bmN0aW9ucyBgcmVhZC50YWJsZWAgYW5kIGByZWFkX2RlbGltYC4gVGhlIGRlbGltZXRlciBpcyBgfGAgY2hhcmFjdGVyIGFuZCB0aGVyZSBhcmUgdHdvIGNvbW1lbnQgbGluZSBvbiB0b3AuIEFzIHlvdSBjYW4gc2VlLCBib3RoIGZ1bmN0aW9ucyBoYXZlIGNvbXBsZXRlbHkgZGlmZmVyZW50IG9wdGlvbnMuIAoKYGBge3Igc29sdXRpb24xLCB3YXJuaW5nPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShkcGx5cikKIyB0aGlzIGlzIGJhc2UgUiBzb2x1dGlvbiBhbmQgaXQgYXV0b21hdGljYWxseSBza2lwcyBmaXJzdCAyIGNvbW1uZW50cyBpbG5lLCBkdWUgdG8gYGNvbW1lbnQuY2hhciA9ICIjImAgZGVmYXVsdCBvcHRpb24KZ3JhZGVzIDwtIHJlYWQudGFibGUoInN0dWRlbnRfZ3JhZGVzLmNzdiIsIHNlcCA9ICJ8IiwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIApncmFkZXMgJT4lIAogIGZpbHRlcihsZXR0ZXJfZ3JhZGU9PSJBIikKCiMgT1IKCmxpYnJhcnkocmVhZHIpCmdyYWRlczIgPC0gcmVhZF9kZWxpbSgic3R1ZGVudF9ncmFkZXMuY3N2IiwgIGRlbGltID0gInwiLCBza2lwPTIpICAjdGhpcyBpcyB0aWR5dmVyc2Ugc29sdXRpb24KZ3JhZGVzMiAlPiUgCiAgZmlsdGVyKGxldHRlcl9ncmFkZT09IkEiKQoKYGBgCgpCZWxvdyBpcyB0aGUgcmVsYXRlZCBzbGlkZSBmcm9tICJJbXBvcnRpbmcgRGF0YSBpbiBSIiBjb3Vyc2UsIENoYXB0ZXIgMi4KCiFbSW1wb3J0IHdpdGggZGVsaW0gb3Igc2VwIG9wdGlvbnNdKGltcG9ydF9kZWxpbS5wbmcpCgojIFF1ZXN0aW9uIDIuIEdlbmVzIGFuZCBwaGVub3R5cGVzCgojIyBBLiBHZW5lcyB3aXRob3V0IHBoZW5vdHlwZQoKVXNpbmcgYGh1bWFuX2dlbmVzYCBhbmQgYHBoZW5vdHlwZXNgIHRhYmxlcyAod2hpY2ggYXJlIGxvYWRlZCBmb3IgeW91KSwgcGxlYXNlIGNhbGN1bGF0ZSB0aGUgZm9sbG93aW5nOiBUaGUgbnVtYmVyIG9mIGdlbmVzIHdoaWNoIGRvZXMgbm90IGhhdmUgYW55IHBoZW5vdHlwZSBhc3NvY2lhdGllZCB3aXRoIHRoZW0uIChwbGVhc2UgdXNlIGBqb2luYCkKCiMjIFNvbHV0aW9uIDIuQQoKYGBge3IgYW5zd2VyMmF9CiMgZ2VuZV9pZCBjb2x1bW4gaXMgdGhlIGtleQojIGxldCdzIHVzZSBhbnRpX2pvaW4gZmlyc3QKYW50aV9qb2luKGh1bWFuX2dlbmVzLHBoZW5vdHlwZXMpICU+JSAKICBjb3VudCgpCgojIE9SIHdlIGNhbiB1c2UgbGVmdF9qb2luCmxlZnRfam9pbihodW1hbl9nZW5lcywgcGhlbm90eXBlcykgJT4lIAogIGZpbHRlcihpcy5uYShwaGVub3R5cGUpKSAlPiUgCiMgIHN1bW1hcmlzZShjb3VudD1uX2Rpc3RpbmN0KGdlbmVfaWQpKQogIGNvdW50KCkKYGBgCgoKIyMgQi4gQmFkIGNocm9tb3NvbWVzCgpVc2luZyBgaHVtYW5fZ2VuZXNgIGFuZCBgcGhlbm90eXBlc2AgdGFibGVzLCBwbGVhc2UgY2FsY3VsYXRlIGhvdyBtYW55IGdlbmVzIGVhY2ggY2hyb21vc29tZSBjYXJyeSAqKndoaWNoIGFyZSBhc3NvY2lhdGVkIHdpdGggYSBwaGVub3R5cGUqKi4gUGxlYXNlIGRvIG5vdCBjb3VudCBhIGdlbmUgbXVsdGlwbGUgdGltZSBpZiBpdCdzIGFzc29jaWF0ZWQgd2l0aCBtdWx0aXBsZSBwaGVub3R5cGVzLgoKRXhwZWN0ZWQgb3V0cHV0OgoKfCBDaHJvbW9zb21lIHwgY291bnQgfAp8LS0tLS0tLS0tLS0tfC0tLS0tLS18CnwgICAgIC4uICAgICB8ICAuLiAgIHwKCiMjIFNvbHV0aW9uIDIuQgoKYGBge3IgYW5zd2VyMmJ9CmlubmVyX2pvaW4oaHVtYW5fZ2VuZXMscGhlbm90eXBlcykgJT4lIAogIGdyb3VwX2J5KGNocikgJT4lIAogIHN1bW1hcmlzZShjb3VudD1uX2Rpc3RpbmN0KGdlbmVfaWQpKQpgYGAKCiMgUXVlc3Rpb24gMy4gQSBCYWQgRGF5IG9mIE1vbnRoCgohW0ZpZ3VyZTogVG90YWwgYW5kIERlbGF5ZWQgbnVtYmVyIG9mIGZsaWdodHMgZm9yIGRheXMgb2YgbW9udGggd2l0aCBoaWdoZXN0IHBlcmNlbnRhZ2VdKGh0dHBzOi8vczMtdXMtd2VzdC0yLmFtYXpvbmF3cy5jb20vdmVyaS1hbmFsaXppL3BlcmNlbnRfZGVsYXkucG5nKQoKVGhlIHBsb3QgZGVzY3JpYmVzICoqdG9wIDEwKiogZGF5cyBvZiBtb250aCB3aGljaCBoYXZlICoqaGlnaGVzdCBwZXJjZW50YWdlKiogb2YgZGVsYXlzIChwZXJjZW50YWdlPSAqbnVtYmVyIG9mIGRlbGF5ZWQgZmxpZ2h0cyBvbiB0aGF0IERheSBvZiBNb250aCogLyAqdG90YWwgbnVtYmVyIG9mIGZsaWdodHMgb24gdGhhdCBEYXkgb2YgTW9udGgqKS4gWW91IGNhbiBjb25zaWRlciBhIGZsaWdodCBhcyAiZGVsYXllZCIgaWYgRGVwYXJ0dXJlIGRlbGF5IGlzIGVxdWFsIHRvIG9yIGdyZWF0ZXIgdGhhbiA1IG1pbnV0ZXMuIEJsdWUgZG90cyBzaG93IHRvdGFsIGZsaWdodHMgYW5kIHJlZCBkb3RzIHNob3cgZGVsYXllZCBmbGlnaHQgY291bnRzLiBQbGVhc2Ugd3JpdGUgZG93biB0aGUgY29kZSBiZWxvdyB3aGljaCBjYW4gcHJvZHVjZSB0aGUgcGxvdCBhYm92ZS4KCiMgU29sdXRpb24gMwoKYGBge3IgYW5zd2VyM30KbGlicmFyeShnZ3Bsb3QyKQpoZmxpZ2h0cyAlPiUgCiAgZ3JvdXBfYnkoRGF5b2ZNb250aCkgJT4lIAogIHN1bW1hcmlzZShuX2ZsaWdodHM9bigpLAogICAgICAgICAgICBuX2RlbGF5cz1zdW0oRGVwRGVsYXkgPiA1LCBuYS5ybT1UUlVFKSwgICMgY291bnRpbmcgZGVsYXlzIHZpYSBsb2dpY2FsIHZlY3RvcgogICAgICAgICAgICBwX2RlbGF5PW5fZGVsYXlzL25fZmxpZ2h0cyoxMDApICU+JSAgICAgICMgcGVyY2VudGFnZSBjYWxjdWxhdGlvbgogIGZpbHRlcihyYW5rKC1wX2RlbGF5KSA8IDExICkgJT4lICAgICMgZXh0cmFjdGluZyB0b3AgMTAgd2l0aCByYW5rKCksIG1pbnVzIG1lYW5zIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWNjb3JkaW5nIHRvIHJldmVyc2Ugb3JkZXIsIGhpZ2hlc3QgcF9kZWxheSB2YWx1ZQojICBhcnJhbmdlKC1wX2RlbGF5KSAlPiUgCiAgZ2dwbG90KGFlcyh4PURheW9mTW9udGgpKSArCiAgZ2VvbV9wb2ludChhZXMoeT1uX2ZsaWdodHMpLGNvbG9yPSJibHVlIikgKwogIGdlb21fcG9pbnQoYWVzKHk9bl9kZWxheXMpLGNvbG9yPSJyZWQiKSArCiAgeWxhYigiTnVtYmVyIG9mIFxuZGVsYXllZCBvciB0b3RhbCBmbGlnaHRzIikgICMgdGhpcyBsYWJlbGluZyB3YXMgbm90IHJlcXVpcmVkCmBgYAoKQnkgdGhlIHdheSwgRGF5T2ZNb250aCBzaG91bGQgbm90IGJlIHRyZWF0ZWQgYXMgbnVtYmVyIGluIHRoaXMgZ3JhcGgsIGl0IHNob3VsZCBiZSB0cmVhdGVkIGFzIGZhY3Rvci4gSWYgeW91IGFkZCBgbXV0YXRlKERheW9mTW9udGg9YXMuZmFjdG9yKERheW9mTW9udGgpKWAganVzdCBiZWZvcmUgYGdyb3VwX2J5YCBsaW5lLCB0aGVuIHlvdSBnZXQgdGhlIGZvbGxvd2luZyBwbG90LCB3aGljaCBpcyBtb3JlIGFjY3VyYXRlLgoKYGBge3IgYW5zd2VyM19hbHRlcm5hdGUsIGVjaG89RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKaGZsaWdodHMgJT4lIAogIG11dGF0ZShEYXlvZk1vbnRoPWFzLmZhY3RvcihEYXlvZk1vbnRoKSkgJT4lIAogIGdyb3VwX2J5KERheW9mTW9udGgpICU+JSAKICBzdW1tYXJpc2Uobl9mbGlnaHRzPW4oKSwKICAgICAgICAgICAgbl9kZWxheXM9c3VtKERlcERlbGF5ID4gNSwgbmEucm09VFJVRSksICAjIGNvdW50aW5nIGRlbGF5cyB2aWEgbG9naWNhbCB2ZWN0b3IKICAgICAgICAgICAgcF9kZWxheT1uX2RlbGF5cy9uX2ZsaWdodHMqMTAwKSAlPiUgICAgICAjIHBlcmNlbnRhZ2UgY2FsY3VsYXRpb24KICBmaWx0ZXIocmFuaygtcF9kZWxheSkgPCAxMSApICU+JSAgICAjIGV4dHJhY3RpbmcgdG9wIDEwIHdpdGggcmFuaygpLCBtaW51cyBtZWFucyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFjY29yZGluZyB0byByZXZlcnNlIG9yZGVyLCBoaWdoZXN0IHBfZGVsYXkgdmFsdWUKICBnZ3Bsb3QoYWVzKHg9RGF5b2ZNb250aCkpICsKICBnZW9tX3BvaW50KGFlcyh5PW5fZmxpZ2h0cyksY29sb3I9ImJsdWUiKSArCiAgZ2VvbV9wb2ludChhZXMoeT1uX2RlbGF5cyksY29sb3I9InJlZCIpICsKICB5bGFiKCJOdW1iZXIgb2YgXG5kZWxheWVkIG9yIHRvdGFsIGZsaWdodHMiKSAgIyB0aGlzIGxhYmVsaW5nIHdhcyBub3QgcmVxdWlyZWQKYGBgCgoKSGVyZSdzIHRoZSBzY3JlZW5zaG90IGZyb20gRGF0YUNhbXAgZXhlcmNpc2Ugd2hpY2ggdXNlcyBwZXJjZW50YWdlIGNhbGN1bGF0aW9uIGFuZCBjb3VudGluZyB3aXRoIGBzdW0oKWAgb3ZlciBsb2dpY2FsIHZlY3RvciB0ZWNobmlxdWUuICJEYXRhIE1hbmlwdWxhdGlvbiBpbiBSIHdpdGggZHBseXIiIGxlY3R1cmUsIENoYXB0ZXIgNSwgRXhlcmNpc2UgdGl0bGUgIlVuaXRlIGFuZCBjb25xdWVyIHVzaW5nIGdyb3VwX2J5IiAKCiFbZHBseXIgcGVyY2VudGFnZSBhbmQgc3VtXShkcGx5cl9wZXJjZW50YWdlLnBuZykKCiMgUXVlc3Rpb24gNC4gQSBCYWQgRGF5IGZvciBFdmVyeW9uZQoKWW91IGFscmVhZHkgc3R1ZGllZCBhbmQgYXJlIGF3YXJlIG9mIGBoZmxpZ2h0c2AgdGFibGUgZnJvbSBEYXRhQ2FtcCBjb3Vyc2VzLiBUd28gbW9yZSB0YWJsZXMgYXJlIGFscmVhZHkgZGVmaW5lZCBmb3IgeW91ICh3aGVuIHlvdSBydW4gdGhlIGZpcnN0IGNodW5rKS4gSW5zdGVhZCBvZiB1c2luZyBsb29rdXAgdGFibGVzIGFzIHlvdSBkaWQgRGF0YUNhbXAsIGxldCdzIHVzZSBgam9pbmAgdG8gY29tYmluZSBhbGwgZGF0YS4gVXNlIGBhaXJsaW5lX2NvZGVzYCBhbmQgYGFpcnBvcnRfY29kZXNgIHRhYmxlcyAoYWxyZWFkeSBsb2FkZWQgZm9yIHlvdSkgdG8gZmluZCBvdXQgdGhlIHdvcnN0IGRheSBmb3IgZWFjaCBhaXJsaW5lLiAqVGhlIHdvcnN0IGRheSogaXMgZGVmaW5lZCBhcyB0aGUgbWF4aW11bSBkZXBhcnR1cmUgZGVsYXksIHNvIHBsZWFzZSBmaW5kIG91dCBtYXggZGVwYXJ0dXJlIGRlbGF5IG9mICoqZWFjaCBhaXJsaW5lKiogYW5kIHRoZW4gam9pbiB3aXRoIG5lY2Vzc2FyeSB0YWJsZXMgdG8gZ2V0IHRoZSBvdXRwdXQgYmVsb3c6CgpFeHBlY3RlZCBvdXRwdXQ6Cgp8IEFpcmxpbmUgfCBEZXN0aW5hdGlvbiB8IE1heCBEZXAgRGVsYXkgfAp8LS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfAp8ICAgLi4gICAgfCAgIC4uICAgICAgICB8ICAgICAgIC4uICAgICAgfAoKIkFpcmxpbmUiIGNvbHVtbiBzaG91bGQgaGF2ZSBjYXJyaWVyJ3MgZnVsbCBuYW1lIGFuZCAiRGVzdGluYXRpb24iIGNvbHVtbiBzaG91bGQgaGF2ZSBmdWxsIG5hbWUgb2YgYWlycG9ydC4KCipIaW50OiogcGxlYXNlIGNvbnNpZGVyIHVzaW5nIGByYW5rKClgIGZ1bmN0aW9uCiAKIyBTb2x1dGlvbiA0CgpgYGB7ciBhbnN3ZXI0fQpoZmxpZ2h0cyAlPiUgCiAgaW5uZXJfam9pbihhaXJsaW5lX2NvZGVzLCBieT1jKCJVbmlxdWVDYXJyaWVyIj0ibmFtZSIpKSAlPiUgICAjIHRoZSBrZXlzIGhhdmUgZGlmZmVyZW50IG5hbWVzCiAgaW5uZXJfam9pbihhaXJwb3J0X2NvZGVzLCBieT1jKCJEZXN0Ij0iZmFhIikpICU+JSAgICAgICAgICAgICAjIHRoZSBrZXlzIGhhdmUgZGlmZmVyZW50IG5hbWVzCiAgZ3JvdXBfYnkoVW5pcXVlQ2FycmllcikgJT4lICAgICAgICAjIGZvciB1c2luZyByYW5rKCkgd2l0aGluIGFpcmxpbmUgZ3JvdXBzLCB3ZSBuZWVkIGdyb3VwX2J5CiAgZmlsdGVyKHJhbmsoLURlcERlbGF5KT09MSkgJT4lICAgICAjIHNlbGVjdCBsaW5lcyB3aG9zZSByYW5rIGlzIDEgaW4gcmV2ZXJzZSAoaGlnaGVzdCBEZXBEZWxheSkKICB1bmdyb3VwKCkgJT4lICAgICAgICAgICAgICAgICAgICAgICMgdGhpcyBpcyBub3QgcmVxdWlyZWQgYnV0IGdlbmVyYXRlcyBjbGVhbmVyIHJlc3VsdAogIHNlbGVjdChBaXJsaW5lPXZhbHVlLERlc3RpbmF0aW9uPW5hbWUsTWF4X0RlcF9EZWxheT1EZXBEZWxheSkgICMgc2VsZWN0IGFuZCByZW5hbWUgYXQgdGhlIHNhbWUgdGltZQpgYGAKCkhlcmUncyB0aGUgc2NyZWVuc2hvdCBmcm9tIERhdGFDYW1wIGV4ZXJjaXNlIHdoaWNoIHVzZXMgYG11dGF0ZShyYW5rPXJhbmsoZGVzYyhuKSkpICU+JSBmaWx0ZXIocmFuaz09MSlgIHRvIHBpY2sgcm93cyB3aXRoIGhpZ2hlc3QgcmFuay4gSW4gb3VyIHNvbHV0aW9uIHdlIHVzZWQgYGZpbHRlcihyYW5rKC1EZXBEZWxheSk9PTEpYCB0byBza2lwIGBtdXRhdGUoKWAgc3RlcC4gIkRhdGEgTWFuaXB1bGF0aW9uIGluIFIgd2l0aCBkcGx5ciIgbGVjdHVyZSwgQ2hhcHRlciA1LCBFeGVyY2lzZSB0aXRsZSAiQWR2YW5jZWQgZ3JvdXBfYnkgZXhlcmNpc2VzIiAKCiFbZHBseXIgcmFuayBhbmQgZmlsdGVyXShkcGx5cl9yYW5rX2ZpbHRlci5wbmcpCg==