library(tidyverse)
library(openintro)
library(RCurl)
library(dplyr)
library(stringr)

Overview

This lab is practice for string manipulation and regular expression pattern matching.

Exercise 1

Using the 173 majors listed in fivethirtyeight.com’s College Majors dataset [https://fivethirtyeight.com/features/the-economic-guide-to-picking-a-college-major/], provide code that identifies the majors that contain either “DATA” or “STATISTICS”

Load the college majors dataset from a hosted “majors-list” csv. This dataset was chosen because it contained all 173 majors and a field for a N/A major as well. The objective is to determine if data and analytics are in any of the major names. All major names are always capitalized in this dataset, so the words to match are “DATA” and “STATISTICS”. The “|” in between the words within the “str_detect” function’s pattern indicates that we want one word or the other. Three majors contained these words.

college_majors_url <- 'https://raw.githubusercontent.com/Megabuster/Data607/refs/heads/main/data/assignment3/majors-list.csv'
raw_text <- getURL(college_majors_url)
college_majors_dataset <- read.csv(text = raw_text)
data_stat_majors <- college_majors_dataset %>% filter(str_detect(Major, 'DATA|STATISTICS'))
data_stat_majors
##   FOD1P                                         Major          Major_Category
## 1  6212 MANAGEMENT INFORMATION SYSTEMS AND STATISTICS                Business
## 2  2101      COMPUTER PROGRAMMING AND DATA PROCESSING Computers & Mathematics
## 3  3702               STATISTICS AND DECISION SCIENCE Computers & Mathematics

Exercise 2

Write code that transforms the data below. input: [1] “bell pepper” “bilberry” “blackberry” “blood orange” “blueberry” “cantaloupe” “chili pepper” “cloudberry”
[9] “elderberry” “lime” “lychee” “mulberry” “olive” “salal berry”

Expected output: c(“bell pepper”, “bilberry”, “blackberry”, “blood orange”, “blueberry”, “cantaloupe”, “chili pepper”, “cloudberry”, “elderberry”, “lime”, “lychee”, “mulberry”, “olive”, “salal berry”)

The input data reads like a vector of values from a dataframe which has been created here as “fruit_vector”.

fruit_df <- data.frame(fruit = c('bell pepper', 'bilberry', 'blackberry', 'blood orange', 'blueberry', 'cantaloupe', 'chili pepper', 'cloudberry', 'elderberry', 'lime', 'lychee', 'mulberry', 'olive', 'salal berry'))
fruit_df
##           fruit
## 1   bell pepper
## 2      bilberry
## 3    blackberry
## 4  blood orange
## 5     blueberry
## 6    cantaloupe
## 7  chili pepper
## 8    cloudberry
## 9    elderberry
## 10         lime
## 11       lychee
## 12     mulberry
## 13        olive
## 14  salal berry
fruit_vector <- fruit_df$fruit
fruit_vector
##  [1] "bell pepper"  "bilberry"     "blackberry"   "blood orange" "blueberry"   
##  [6] "cantaloupe"   "chili pepper" "cloudberry"   "elderberry"   "lime"        
## [11] "lychee"       "mulberry"     "olive"        "salal berry"

The expected output appears to be a string representation of the character vector. One way to achieve this would be to use “dQuote” to add double quotes to each vector item. The “paste” function then merges the elements into a single string separated by a comma via the collapse property. The variable “fruit_str_parens” collects “fruit_str”, “c”, and “parentheses” that are all present in the expected output. From there, perform another “paste”, but this time on “fruit_str_parens” and make sure no new separators are added.

fruit_str <- paste(dQuote(fruit_vector), collapse = ", ")
fruit_str
## [1] "\"bell pepper\", \"bilberry\", \"blackberry\", \"blood orange\", \"blueberry\", \"cantaloupe\", \"chili pepper\", \"cloudberry\", \"elderberry\", \"lime\", \"lychee\", \"mulberry\", \"olive\", \"salal berry\""
fruit_str_parens <- c('c(', fruit_str, ')')
fruit_str_parens
## [1] "c("                                                                                                                                                                                                              
## [2] "\"bell pepper\", \"bilberry\", \"blackberry\", \"blood orange\", \"blueberry\", \"cantaloupe\", \"chili pepper\", \"cloudberry\", \"elderberry\", \"lime\", \"lychee\", \"mulberry\", \"olive\", \"salal berry\""
## [3] ")"
final_fruit <- paste(fruit_str_parens, collapse = "")
final_fruit
## [1] "c(\"bell pepper\", \"bilberry\", \"blackberry\", \"blood orange\", \"blueberry\", \"cantaloupe\", \"chili pepper\", \"cloudberry\", \"elderberry\", \"lime\", \"lychee\", \"mulberry\", \"olive\", \"salal berry\")"

Exercise 3

Describe, in words, what these expressions will match:

  1. “(.)\1\1” This expression did not match anything when testing it. If this was meant to be “(.)\\1\\1” with double backslashes, it would match three of the same character in a row. This would be like ‘aaa’.

  2. “(.)(.)\\2\\1” This expression matches texts that have two of the same character in between two of the same character (can be the same character as the other pair). This would be like ‘baab’ or ‘aaaa’.

  3. “(..)\1” This expression did not match anything when testing it. If this was meant to be “(..)\\1” with double backslashes, it would match patterns where a pair of characters is repeated immediately after. For example, this could be ‘baba’ or ‘aaaa’.

  4. “(.).\\1.\\1” This expression matches a pattern where every other character is the same three times. The alternating character in between these three instances of the character can be any character. This means a specific character followed by any character, followed by the first character, followed by any character, followed by the first character again. The form could be “ababa”, “aaaaa”, or “aaaba”.

  5. “(.)(.)(.).*\\3\\2\\1” This expression matches three characters in a specific order with the same three characters in reverse at a later point within the text. The repeated reverse characters can either be immediately after the original order or have multiple characters in between. For example, these could be “abccba” or “abcbtwcba”.

Exercise 4

Construct regular expressions to match words that:

  1. Start and end with the same character.

^(.).*\\1$

This expression assumes that there can be any number of characters, including none, beteween the repeating character. The “^” in the beginning ensures this character is the first one and the “$” ensures it ends with that same character.

  1. Contain a repeated pair of letters (e.g. “church” contains “ch” repeated twice.)

(..).*\\1

No specification was given that the repeated pair of letters needed to be apart or be the start or end of the word. The “.*” in between means any number, including none, of characters can occur in between the matching pairs.

  1. Contain one letter repeated in at least three places (e.g. “eleven” contains three “e”s.)

(.).\\1.\\1

This expression detects the first time a character appears three times within the word regardless of where in the word each occurrence is.

Conclusions

Data can be found in various forms which then need to be converted into forms that can be used for analysis. Regular expressions are useful for extracting data from sources such as raw HTML. They can also be used to match patterns to check if certain words or phrases appear for an analysis.

LS0tDQp0aXRsZTogIldlZWsgMyBhc3NpZ25tZW50Ig0KYXV0aG9yOiAiTGF3cmVuY2UgWXUiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydA0KLS0tDQoNCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkob3BlbmludHJvKQ0KbGlicmFyeShSQ3VybCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpgYGANCg0KIyMjIE92ZXJ2aWV3DQpUaGlzIGxhYiBpcyBwcmFjdGljZSBmb3Igc3RyaW5nIG1hbmlwdWxhdGlvbiBhbmQgcmVndWxhciBleHByZXNzaW9uIHBhdHRlcm4gbWF0Y2hpbmcuIA0KDQojIyMgRXhlcmNpc2UgMQ0KVXNpbmcgdGhlIDE3MyBtYWpvcnMgbGlzdGVkIGluIGZpdmV0aGlydHllaWdodC5jb23igJlzIENvbGxlZ2UgTWFqb3JzIGRhdGFzZXQgW2h0dHBzOi8vZml2ZXRoaXJ0eWVpZ2h0LmNvbS9mZWF0dXJlcy90aGUtZWNvbm9taWMtZ3VpZGUtdG8tcGlja2luZy1hLWNvbGxlZ2UtbWFqb3IvXSwgcHJvdmlkZSBjb2RlIHRoYXQgaWRlbnRpZmllcyB0aGUgbWFqb3JzIHRoYXQgY29udGFpbiBlaXRoZXIgIkRBVEEiIG9yICJTVEFUSVNUSUNTIg0KDQpMb2FkIHRoZSBjb2xsZWdlIG1ham9ycyBkYXRhc2V0IGZyb20gYSBob3N0ZWQgIm1ham9ycy1saXN0IiBjc3YuIFRoaXMgZGF0YXNldCB3YXMgY2hvc2VuIGJlY2F1c2UgaXQgY29udGFpbmVkIGFsbCAxNzMgbWFqb3JzIGFuZCBhIGZpZWxkIGZvciBhIE4vQSBtYWpvciBhcyB3ZWxsLiBUaGUgb2JqZWN0aXZlIGlzIHRvIGRldGVybWluZSBpZiBkYXRhIGFuZCBhbmFseXRpY3MgYXJlIGluIGFueSBvZiB0aGUgbWFqb3IgbmFtZXMuIEFsbCBtYWpvciBuYW1lcyBhcmUgYWx3YXlzIGNhcGl0YWxpemVkIGluIHRoaXMgZGF0YXNldCwgc28gdGhlIHdvcmRzIHRvIG1hdGNoIGFyZSAiREFUQSIgYW5kICJTVEFUSVNUSUNTIi4gVGhlICJ8IiBpbiBiZXR3ZWVuIHRoZSB3b3JkcyB3aXRoaW4gdGhlICJzdHJfZGV0ZWN0IiBmdW5jdGlvbidzIHBhdHRlcm4gaW5kaWNhdGVzIHRoYXQgd2Ugd2FudCBvbmUgd29yZCBvciB0aGUgb3RoZXIuIFRocmVlIG1ham9ycyBjb250YWluZWQgdGhlc2Ugd29yZHMuDQoNCmBgYHtyIGV4ZXJjaXNlMS1maW5kLW1ham9ycy1wYXR0ZXJufQ0KY29sbGVnZV9tYWpvcnNfdXJsIDwtICdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vTWVnYWJ1c3Rlci9EYXRhNjA3L3JlZnMvaGVhZHMvbWFpbi9kYXRhL2Fzc2lnbm1lbnQzL21ham9ycy1saXN0LmNzdicNCnJhd190ZXh0IDwtIGdldFVSTChjb2xsZWdlX21ham9yc191cmwpDQpjb2xsZWdlX21ham9yc19kYXRhc2V0IDwtIHJlYWQuY3N2KHRleHQgPSByYXdfdGV4dCkNCmRhdGFfc3RhdF9tYWpvcnMgPC0gY29sbGVnZV9tYWpvcnNfZGF0YXNldCAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoTWFqb3IsICdEQVRBfFNUQVRJU1RJQ1MnKSkNCmRhdGFfc3RhdF9tYWpvcnMNCmBgYA0KDQojIyMgRXhlcmNpc2UgMg0KV3JpdGUgY29kZSB0aGF0IHRyYW5zZm9ybXMgdGhlIGRhdGEgYmVsb3cuDQppbnB1dDogDQogWzFdICJiZWxsIHBlcHBlciIgICJiaWxiZXJyeSIgICAgICJibGFja2JlcnJ5IiAgICJibG9vZCBvcmFuZ2UiICJibHVlYmVycnkiICAgICJjYW50YWxvdXBlIiAgICJjaGlsaSBwZXBwZXIiICJjbG91ZGJlcnJ5IiAgDQogWzldICJlbGRlcmJlcnJ5IiAgICJsaW1lIiAgICAgICAgICJseWNoZWUiICAgICAgICJtdWxiZXJyeSIgICAgICJvbGl2ZSIgICAgICAgICJzYWxhbCBiZXJyeSIgDQoNCkV4cGVjdGVkIG91dHB1dDogYygiYmVsbCBwZXBwZXIiLCAiYmlsYmVycnkiLCAiYmxhY2tiZXJyeSIsICJibG9vZCBvcmFuZ2UiLCAiYmx1ZWJlcnJ5IiwgImNhbnRhbG91cGUiLCAiY2hpbGkgcGVwcGVyIiwgImNsb3VkYmVycnkiLCAiZWxkZXJiZXJyeSIsICJsaW1lIiwgImx5Y2hlZSIsICJtdWxiZXJyeSIsICJvbGl2ZSIsICJzYWxhbCBiZXJyeSIpDQoNClRoZSBpbnB1dCBkYXRhIHJlYWRzIGxpa2UgYSB2ZWN0b3Igb2YgdmFsdWVzIGZyb20gYSBkYXRhZnJhbWUgd2hpY2ggaGFzIGJlZW4gY3JlYXRlZCBoZXJlIGFzICJmcnVpdF92ZWN0b3IiLiANCg0KYGBge3IgaW5wdXRfZnJ1aXRfdmVjdG9yfQ0KZnJ1aXRfZGYgPC0gZGF0YS5mcmFtZShmcnVpdCA9IGMoJ2JlbGwgcGVwcGVyJywgJ2JpbGJlcnJ5JywgJ2JsYWNrYmVycnknLCAnYmxvb2Qgb3JhbmdlJywgJ2JsdWViZXJyeScsICdjYW50YWxvdXBlJywgJ2NoaWxpIHBlcHBlcicsICdjbG91ZGJlcnJ5JywgJ2VsZGVyYmVycnknLCAnbGltZScsICdseWNoZWUnLCAnbXVsYmVycnknLCAnb2xpdmUnLCAnc2FsYWwgYmVycnknKSkNCmZydWl0X2RmDQpmcnVpdF92ZWN0b3IgPC0gZnJ1aXRfZGYkZnJ1aXQNCmZydWl0X3ZlY3Rvcg0KDQpgYGANClRoZSBleHBlY3RlZCBvdXRwdXQgYXBwZWFycyB0byBiZSBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcmFjdGVyIHZlY3Rvci4gT25lIHdheSB0byBhY2hpZXZlIHRoaXMgd291bGQgYmUgdG8gdXNlICJkUXVvdGUiIHRvIGFkZCBkb3VibGUgcXVvdGVzIHRvIGVhY2ggdmVjdG9yIGl0ZW0uIFRoZSAicGFzdGUiIGZ1bmN0aW9uIHRoZW4gbWVyZ2VzIHRoZSBlbGVtZW50cyBpbnRvIGEgc2luZ2xlIHN0cmluZyBzZXBhcmF0ZWQgYnkgYSBjb21tYSB2aWEgdGhlIGNvbGxhcHNlIHByb3BlcnR5LiBUaGUgdmFyaWFibGUgImZydWl0X3N0cl9wYXJlbnMiIGNvbGxlY3RzICJmcnVpdF9zdHIiLCAiYyIsIGFuZCAicGFyZW50aGVzZXMiIHRoYXQgYXJlIGFsbCBwcmVzZW50IGluIHRoZSBleHBlY3RlZCBvdXRwdXQuIEZyb20gdGhlcmUsIHBlcmZvcm0gYW5vdGhlciAicGFzdGUiLCBidXQgdGhpcyB0aW1lIG9uICJmcnVpdF9zdHJfcGFyZW5zIiBhbmQgbWFrZSBzdXJlIG5vIG5ldyBzZXBhcmF0b3JzIGFyZSBhZGRlZC4NCmBgYHtyIGNvbnZlcnQtdG8tc3RyaW5nfQ0KZnJ1aXRfc3RyIDwtIHBhc3RlKGRRdW90ZShmcnVpdF92ZWN0b3IpLCBjb2xsYXBzZSA9ICIsICIpDQpmcnVpdF9zdHINCg0KZnJ1aXRfc3RyX3BhcmVucyA8LSBjKCdjKCcsIGZydWl0X3N0ciwgJyknKQ0KZnJ1aXRfc3RyX3BhcmVucw0KDQpmaW5hbF9mcnVpdCA8LSBwYXN0ZShmcnVpdF9zdHJfcGFyZW5zLCBjb2xsYXBzZSA9ICIiKQ0KZmluYWxfZnJ1aXQNCmBgYA0KDQojIyMgRXhlcmNpc2UgMw0KRGVzY3JpYmUsIGluIHdvcmRzLCB3aGF0IHRoZXNlIGV4cHJlc3Npb25zIHdpbGwgbWF0Y2g6DQoNCjEuICIoLilcMVwxIg0KVGhpcyBleHByZXNzaW9uIGRpZCBub3QgbWF0Y2ggYW55dGhpbmcgd2hlbiB0ZXN0aW5nIGl0LiBJZiB0aGlzIHdhcyBtZWFudCB0byBiZSAiKC4pXFxcXDFcXFxcMSIgd2l0aCBkb3VibGUgYmFja3NsYXNoZXMsIGl0IHdvdWxkIG1hdGNoIHRocmVlIG9mIHRoZSBzYW1lIGNoYXJhY3RlciBpbiBhIHJvdy4gVGhpcyB3b3VsZCBiZSBsaWtlICdhYWEnLg0KDQoyLiAiKC4pKC4pXFxcXDJcXFxcMSINClRoaXMgZXhwcmVzc2lvbiBtYXRjaGVzIHRleHRzIHRoYXQgaGF2ZSB0d28gb2YgdGhlIHNhbWUgY2hhcmFjdGVyIGluIGJldHdlZW4gdHdvIG9mIHRoZSBzYW1lIGNoYXJhY3RlciAoY2FuIGJlIHRoZSBzYW1lIGNoYXJhY3RlciBhcyB0aGUgb3RoZXIgcGFpcikuIFRoaXMgd291bGQgYmUgbGlrZSAnYmFhYicgb3IgJ2FhYWEnLg0KDQozLiAiKC4uKVwxIg0KVGhpcyBleHByZXNzaW9uIGRpZCBub3QgbWF0Y2ggYW55dGhpbmcgd2hlbiB0ZXN0aW5nIGl0LiBJZiB0aGlzIHdhcyBtZWFudCB0byBiZSAiKC4uKVxcXFwxIiB3aXRoIGRvdWJsZSBiYWNrc2xhc2hlcywgaXQgd291bGQgbWF0Y2ggcGF0dGVybnMgd2hlcmUgYSBwYWlyIG9mIGNoYXJhY3RlcnMgaXMgcmVwZWF0ZWQgaW1tZWRpYXRlbHkgYWZ0ZXIuIEZvciBleGFtcGxlLCB0aGlzIGNvdWxkIGJlICdiYWJhJyBvciAnYWFhYScuIA0KDQo0LiAiKC4pLlxcXFwxLlxcXFwxIg0KVGhpcyBleHByZXNzaW9uIG1hdGNoZXMgYSBwYXR0ZXJuIHdoZXJlIGV2ZXJ5IG90aGVyIGNoYXJhY3RlciBpcyB0aGUgc2FtZSB0aHJlZSB0aW1lcy4gVGhlIGFsdGVybmF0aW5nIGNoYXJhY3RlciBpbiBiZXR3ZWVuIHRoZXNlIHRocmVlIGluc3RhbmNlcyBvZiB0aGUgY2hhcmFjdGVyIGNhbiBiZSBhbnkgY2hhcmFjdGVyLiBUaGlzIG1lYW5zIGEgc3BlY2lmaWMgY2hhcmFjdGVyIGZvbGxvd2VkIGJ5IGFueSBjaGFyYWN0ZXIsIGZvbGxvd2VkIGJ5IHRoZSBmaXJzdCBjaGFyYWN0ZXIsIGZvbGxvd2VkIGJ5IGFueSBjaGFyYWN0ZXIsIGZvbGxvd2VkIGJ5IHRoZSBmaXJzdCBjaGFyYWN0ZXIgYWdhaW4uIFRoZSBmb3JtIGNvdWxkIGJlICJhYmFiYSIsICJhYWFhYSIsIG9yICJhYWFiYSIuDQoNCjUuICIoLikoLikoLikuKlxcXFwzXFxcXDJcXFxcMSINClRoaXMgZXhwcmVzc2lvbiBtYXRjaGVzIHRocmVlIGNoYXJhY3RlcnMgaW4gYSBzcGVjaWZpYyBvcmRlciB3aXRoIHRoZSBzYW1lIHRocmVlIGNoYXJhY3RlcnMgaW4gcmV2ZXJzZSBhdCBhIGxhdGVyIHBvaW50IHdpdGhpbiB0aGUgdGV4dC4gVGhlIHJlcGVhdGVkIHJldmVyc2UgY2hhcmFjdGVycyBjYW4gZWl0aGVyIGJlIGltbWVkaWF0ZWx5IGFmdGVyIHRoZSBvcmlnaW5hbCBvcmRlciBvciBoYXZlIG11bHRpcGxlIGNoYXJhY3RlcnMgaW4gYmV0d2Vlbi4gRm9yIGV4YW1wbGUsIHRoZXNlIGNvdWxkIGJlICJhYmNjYmEiIG9yICJhYmNidHdjYmEiLg0KDQojIyMgRXhlcmNpc2UgNA0KQ29uc3RydWN0IHJlZ3VsYXIgZXhwcmVzc2lvbnMgdG8gbWF0Y2ggd29yZHMgdGhhdDoNCg0KMS4gU3RhcnQgYW5kIGVuZCB3aXRoIHRoZSBzYW1lIGNoYXJhY3Rlci4NCg0KXiguKS4qXFxcXDEkDQoNClRoaXMgZXhwcmVzc2lvbiBhc3N1bWVzIHRoYXQgdGhlcmUgY2FuIGJlIGFueSBudW1iZXIgb2YgY2hhcmFjdGVycywgaW5jbHVkaW5nIG5vbmUsIGJldGV3ZWVuIHRoZSByZXBlYXRpbmcgY2hhcmFjdGVyLiBUaGUgIl4iIGluIHRoZSBiZWdpbm5pbmcgZW5zdXJlcyB0aGlzIGNoYXJhY3RlciBpcyB0aGUgZmlyc3Qgb25lIGFuZCB0aGUgIiQiIGVuc3VyZXMgaXQgZW5kcyB3aXRoIHRoYXQgc2FtZSBjaGFyYWN0ZXIuDQoNCjIuIENvbnRhaW4gYSByZXBlYXRlZCBwYWlyIG9mIGxldHRlcnMgKGUuZy4g4oCcY2h1cmNo4oCdIGNvbnRhaW5zIOKAnGNo4oCdIHJlcGVhdGVkIHR3aWNlLikNCg0KKC4uKS4qXFxcXDENCg0KTm8gc3BlY2lmaWNhdGlvbiB3YXMgZ2l2ZW4gdGhhdCB0aGUgcmVwZWF0ZWQgcGFpciBvZiBsZXR0ZXJzIG5lZWRlZCB0byBiZSBhcGFydCBvciBiZSB0aGUgc3RhcnQgb3IgZW5kIG9mIHRoZSB3b3JkLiBUaGUgIi4qIiBpbiBiZXR3ZWVuIG1lYW5zIGFueSBudW1iZXIsIGluY2x1ZGluZyBub25lLCBvZiBjaGFyYWN0ZXJzIGNhbiBvY2N1ciBpbiBiZXR3ZWVuIHRoZSBtYXRjaGluZyBwYWlycy4NCg0KMy4gQ29udGFpbiBvbmUgbGV0dGVyIHJlcGVhdGVkIGluIGF0IGxlYXN0IHRocmVlIHBsYWNlcyAoZS5nLiDigJxlbGV2ZW7igJ0gY29udGFpbnMgdGhyZWUg4oCcZeKAnXMuKQ0KDQooLikuKlxcXFwxLipcXFxcMQ0KDQpUaGlzIGV4cHJlc3Npb24gZGV0ZWN0cyB0aGUgZmlyc3QgdGltZSBhIGNoYXJhY3RlciBhcHBlYXJzIHRocmVlIHRpbWVzIHdpdGhpbiB0aGUgd29yZCByZWdhcmRsZXNzIG9mIHdoZXJlIGluIHRoZSB3b3JkIGVhY2ggb2NjdXJyZW5jZSBpcy4NCg0KIyMjIENvbmNsdXNpb25zIA0KRGF0YSBjYW4gYmUgZm91bmQgaW4gdmFyaW91cyBmb3JtcyB3aGljaCB0aGVuIG5lZWQgdG8gYmUgY29udmVydGVkIGludG8gZm9ybXMgdGhhdCBjYW4gYmUgdXNlZCBmb3IgYW5hbHlzaXMuIFJlZ3VsYXIgZXhwcmVzc2lvbnMgYXJlIHVzZWZ1bCBmb3IgZXh0cmFjdGluZyBkYXRhIGZyb20gc291cmNlcyBzdWNoIGFzIHJhdyBIVE1MLiBUaGV5IGNhbiBhbHNvIGJlIHVzZWQgdG8gbWF0Y2ggcGF0dGVybnMgdG8gY2hlY2sgaWYgY2VydGFpbiB3b3JkcyBvciBwaHJhc2VzIGFwcGVhciBmb3IgYW4gYW5hbHlzaXMuDQoNCg0K