HIGHLIGHTING THE OLD SCHOOL WAY

To implement this idea, we don’t need any fancy packages other than ggplot2. The steps are simple:

  1. Using ggplot2, create a plot with your full data set in grey.
  2. Create a new data frame that has been subset to only include the data which you would like to highlight.
  3. Add the highlighted data on to your plot created in step 1. Set the color to something other than grey.
  4. Celebrate!

Example

For our example, we are going to examine the crime incident dataset from Seattle 911 Calls on data.gov. Note that I have covered this data set through multiple blog posts already such as map plots in R and time based heat maps.
Install and Load Libraries

install.packages("ggplot2")
Installing package into <U+393C><U+3E31>C:/Users/jafareem/Documents/R/win-library/3.5<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.5/ggplot2_3.0.0.zip'
Content type 'application/zip' length 3579751 bytes (3.4 MB)
downloaded 3.4 MB
package ‘ggplot2’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\jafareem\AppData\Local\Temp\RtmpcZQ3iW\downloaded_packages
library(lubridate)
package <U+393C><U+3E31>lubridate<U+393C><U+3E32> was built under R version 3.5.1
Attaching package: <U+393C><U+3E31>lubridate<U+393C><U+3E32>

The following object is masked from <U+393C><U+3E31>package:base<U+393C><U+3E32>:

    date
library(ggplot2)
library(ggmap)
package <U+393C><U+3E31>ggmap<U+393C><U+3E32> was built under R version 3.5.1Google Maps API Terms of Service: http://developers.google.com/maps/terms.
Please cite ggmap if you use it: see citation('ggmap') for details.
library(dplyr)

Attaching package: <U+393C><U+3E31>dplyr<U+393C><U+3E32>

The following objects are masked from <U+393C><U+3E31>package:lubridate<U+393C><U+3E32>:

    intersect, setdiff, union

The following objects are masked from <U+393C><U+3E31>package:stats<U+393C><U+3E32>:

    filter, lag

The following objects are masked from <U+393C><U+3E31>package:base<U+393C><U+3E32>:

    intersect, setdiff, setequal, union
library(data.table)
data.table 1.11.4  Latest news: http://r-datatable.com

Attaching package: <U+393C><U+3E31>data.table<U+393C><U+3E32>

The following objects are masked from <U+393C><U+3E31>package:dplyr<U+393C><U+3E32>:

    between, first, last

The following objects are masked from <U+393C><U+3E31>package:lubridate<U+393C><U+3E32>:

    hour, isoweek, mday, minute, month, quarter,
    second, wday, week, yday, year
library(ggrepel)
library(magrittr)

Attaching package: <U+393C><U+3E31>magrittr<U+393C><U+3E32>

The following object is masked from <U+393C><U+3E31>package:ggmap<U+393C><U+3E32>:

    inset

Download the Data

incidents= fread('https://raw.githubusercontent.com/lgellis/MiscTutorial/master/ggmap/i2Sample.csv', stringsAsFactors = FALSE)
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
  2 3348k    2 97435    0     0  35225      0  0:01:37  0:00:02  0:01:35 35225
  9 3348k    9  319k    0     0  86779      0  0:00:39  0:00:03  0:00:36 86779
 14 3348k   14  495k    0     0   103k      0  0:00:32  0:00:04  0:00:28  103k
 20 3348k   20  671k    0     0   116k      0  0:00:28  0:00:05  0:00:23  153k
 25 3348k   25  847k    0     0   125k      0  0:00:26  0:00:06  0:00:20  178k
 30 3348k   30 1023k    0     0   131k      0  0:00:25  0:00:07  0:00:18  185k
 35 3348k   35 1199k    0     0   136k      0  0:00:24  0:00:08  0:00:16  176k
 41 3348k   41 1375k    0     0   140k      0  0:00:23  0:00:09  0:00:14  176k
 46 3348k   46 1551k    0     0   144k      0  0:00:23  0:00:10  0:00:13  176k
 51 3348k   51 1727k    0     0   146k      0  0:00:22  0:00:11  0:00:11  176k
 56 3348k   56 1903k    0     0   149k      0  0:00:22  0:00:12  0:00:10  176k
 62 3348k   62 2079k    0     0   151k      0  0:00:22  0:00:13  0:00:09  176k
 67 3348k   67 2255k    0     0   152k      0  0:00:21  0:00:14  0:00:07  176k
 72 3348k   72 2431k    0     0   154k      0  0:00:21  0:00:15  0:00:06  176k
 77 3348k   77 2607k    0     0   155k      0  0:00:21  0:00:16  0:00:05  176k
 83 3348k   83 2783k    0     0   156k      0  0:00:21  0:00:17  0:00:04  176k
 88 3348k   88 2975k    0     0   158k      0  0:00:21  0:00:18  0:00:03  179k
 94 3348k   94 3151k    0     0   159k      0  0:00:21  0:00:19  0:00:02  179k
 99 3348k   99 3327k    0     0   160k      0  0:00:20  0:00:20 --:--:--  179k
100 3348k  100 3348k    0     0   160k      0  0:00:20  0:00:20 --:--:--  180k
str(incidents) 
Classes ‘data.table’ and 'data.frame':  50000 obs. of  20 variables:
 $ V1                         : int  450024 1028516 1319344 1334148 32080 326478 1010497 1095263 1251322 1278385 ...
 $ CAD.CDW.ID                 : int  813558 1002946 1036985 1053917 48491 681155 931030 2032781 2200447 2257162 ...
 $ CAD.Event.Number           :integer64 12000168676 12000438147 15000080173 15000105067 10000294206 11000387576 12000351857 17000233529 ... 
 $ General.Offense.Number     : int  2012168676 2012438147 201580173 2015105067 2010294206 2011387576 2012351857 2017233529 201847957 201528719 ...
 $ Event.Clearance.Code       : chr  "064" "100" "161" "100" ...
 $ Event.Clearance.Description: chr  "SHOPLIFT" "FRAUD (INCLUDING IDENTITY THEFT)" "TRESPASS" "FRAUD (INCLUDING IDENTITY THEFT)" ...
 $ Event.Clearance.SubGroup   : chr  "THEFT" "FRAUD CALLS" "TRESPASS" "FRAUD CALLS" ...
 $ Event.Clearance.Group      : chr  "SHOPLIFTING" "FRAUD CALLS" "TRESPASS" "FRAUD CALLS" ...
 $ Event.Clearance.Date       : chr  "05/31/2012 06:00:00 PM" "12/24/2012 11:14:00 AM" "03/11/2015 12:45:00 PM" "03/31/2015 04:56:00 PM" ...
 $ Hundred.Block.Location     : chr  "39XX BLOCK OF S OTHELLO ST" "27XX BLOCK OF ALKI AVE SW" "6XX BLOCK OF NW MARKET ST" "77XX BLOCK OF RAINIER AV S" ...
 $ District.Sector            : chr  "S" "W" "B" "S" ...
 $ Zone.Beat                  : chr  "S1" "W1" "B2" "S2" ...
 $ Census.Tract               : chr  "11000.1011" "9701.2000" "4800.4000" "11102.4008" ...
 $ Longitude                  : num  -122 -122 -122 -122 -122 ...
 $ Latitude                   : num  47.5 47.6 47.7 47.5 47.6 ...
 $ Incident.Location          : chr  "(47.537044021, -122.282344886)" "(47.579317217, -122.409989598)" "(47.668651602, -122.364558421)" "(47.533143434, -122.269986901)" ...
 $ Initial.Type.Description   : chr  "" "TRU - FORGERY/CHKS/BUNCO/SCAMS/ID THEFT" "BURG - RES (INCL UNOCC STRUCTURES ON PROP)" "FRAUD - FORGERY,BUNCO, SCAMS, ID THEFT, ETC" ...
 $ Initial.Type.Subgroup      : chr  "" "FRAUD CALLS" "BURGLARY" "FRAUD CALLS" ...
 $ Initial.Type.Group         : chr  "" "FRAUD CALLS" "RESIDENTIAL BURGLARIES" "FRAUD CALLS" ...
 $ At.Scene.Time              : chr  "" "12/24/2012 10:33:00 AM" "" "" ...
 - attr(*, ".internal.selfref")=<externalptr> 
attach(incidents)
# Create some color variables for graphing later
custGrey = "#A9A9A9"
#add year to the incidents data frame
incidents$ymd <-mdy_hms(Event.Clearance.Date)
incidents$month <- lubridate::month(incidents$ymd)
incidents$year <- year(incidents$ymd)
incidents$wday <- lubridate::wday(incidents$ymd, label = TRUE)
incidents$hour <- hour(incidents$ymd)
#Create a more manageable data frame with only 2017 data
i2 <- incidents[year>=2017, ]
#Only include complete cases
i2[complete.cases(i2), ]
attach(i2)
The following objects are masked from incidents:

    At.Scene.Time, CAD.CDW.ID, CAD.Event.Number,
    Census.Tract, District.Sector,
    Event.Clearance.Code, Event.Clearance.Date,
    Event.Clearance.Description,
    Event.Clearance.Group,
    Event.Clearance.SubGroup,
    General.Offense.Number,
    Hundred.Block.Location, Incident.Location,
    Initial.Type.Description,
    Initial.Type.Group, Initial.Type.Subgroup,
    Latitude, Longitude, V1, Zone.Beat
head(i2)

Create a basic time series plot showing the count of 911 event types by month.

#Group the data into a new data frame which has the count of events per month by subgroup
groupSummaries <- i2 %>%
  group_by(month, Event.Clearance.SubGroup) %>%
  summarize(N = length(Event.Clearance.SubGroup))
#View the new data set
head(groupSummaries, n=100)
attach(groupSummaries)
The following objects are masked from i2:

    Event.Clearance.SubGroup, month

The following object is masked from incidents:

    Event.Clearance.SubGroup
#Graph the data set through ggplot 2
ggplot(groupSummaries, aes(x=month, y=N, color=Event.Clearance.SubGroup) )+ 
  geom_line() +
  theme(legend.position="bottom",legend.text=element_text(size=7),
        legend.title = element_blank()) +
  scale_x_discrete(name ="Month", 
                 limits=c(3,6,9,12))

Create a Graph Highlighting Data with a Max Month Count of 95 or Greater

# Create a data frame with only events types that have had a peak of 95 calls in a month or more
groupSummariesF <- groupSummaries %>%
  group_by(Event.Clearance.SubGroup) %>% 
  filter(max(N) > 95) %>%
  ungroup()
head(groupSummariesF)
# Create a layered plot with one layer of grey data for the full data set and one layer of color data for the subset data set 
ggplot() +
  geom_line(aes(month, N, group = Event.Clearance.SubGroup), 
            data = groupSummaries, colour = alpha("grey", 0.7)) +
  geom_line(aes(month, N, group = Event.Clearance.SubGroup, colour = Event.Clearance.SubGroup), 
            data = groupSummariesF) +  
  scale_x_discrete(name ="Month", 
                   limits=c(3,6,9,12)) +
  theme(legend.position="bottom",legend.text=element_text(size=7),
        legend.title = element_blank())

HIGHLIGHTING THE NEW SCHOOL WAY

While the above methodology is quite easy, it can be a bit of a pain at times to create and add the new data frame. Further, you have to tinker more with the labelling to really call out the highlighted data points.

Thanks to Hiroaki Yutani, we now have the gghighlight package which does most of the work for us with a small function call!! Please note that a lot of this code was created by looking at examples on her introduction document.

The new school way is even simplier:

  1. Using ggplot2, create a plot with your full data set.
  2. Add the gghighlight() function to your plot with the conditions set to identify your subset.
  3. Celebrate! This was one less step AND we got labels!

Example

For our first example, we are going to create the same time series graph from above. However, we are going to perform the highlighting with gghighlight vs manual layering.

# Install the gghighlight package
#install.packages("gghighlight")
#library(gghighlight)
# Create the highlighted graph
ggplot(groupSummaries, aes(month, N, colour = Event.Clearance.SubGroup)) +
  geom_line() + 
  gghighlight(max(N) > 95,  label_key = Event.Clearance.SubGroup) +  
  scale_x_discrete(name ="Month", 
                   limits=c(3,6,9,12))

More Examples

Well that was so easy, we are going to try a few more ggmap plot types to see how we fare. Below show both a scatterplot and histogram chart.

# Try a scatterplot chart
ggplot(groupSummaries, aes(month, N, colour = Event.Clearance.SubGroup, use_group_by=FALSE)) +
  geom_point() +
  gghighlight(N > 200,  label_key = Event.Clearance.SubGroup) + 
  scale_x_discrete(name ="Month",
                   limits=c(3,6,9,12))
You set use_group_by = TRUE, but grouped calculation failed.
Falling back to ungrouped filter operation...

# Try a histogram chart
ggplot(groupSummaries, aes(N, fill = Event.Clearance.SubGroup)) +
  geom_histogram() +
  theme(legend.position="bottom",legend.text=element_text(size=7),
        legend.title = element_blank()) +
  gghighlight(N > 100,  label_key = Event.Clearance.SubGroup, use_group_by = FALSE) +
  facet_wrap(~ Event.Clearance.SubGroup)

LS0tDQp0aXRsZTogIkhpZ2hsaWdodGluZyB3aXRoIGdncGxvdDIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojSElHSExJR0hUSU5HIFRIRSBPTEQgU0NIT09MIFdBWQ0KDQpUbyBpbXBsZW1lbnQgdGhpcyBpZGVhLCB3ZSBkb24ndCBuZWVkIGFueSBmYW5jeSBwYWNrYWdlcyBvdGhlciB0aGFuIGdncGxvdDIuICBUaGUgc3RlcHMgYXJlIHNpbXBsZToNCg0KMS4gVXNpbmcgZ2dwbG90MiwgY3JlYXRlIGEgcGxvdCB3aXRoIHlvdXIgZnVsbCBkYXRhIHNldCBpbiBncmV5Lg0KMi4gQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgdGhhdCBoYXMgYmVlbiBzdWJzZXQgdG8gb25seSBpbmNsdWRlIHRoZSBkYXRhIHdoaWNoIHlvdSB3b3VsZCBsaWtlIHRvIGhpZ2hsaWdodC4gIA0KMy4gQWRkIHRoZSBoaWdobGlnaHRlZCBkYXRhIG9uIHRvIHlvdXIgcGxvdCBjcmVhdGVkIGluIHN0ZXAgMS4gIFNldCB0aGUgY29sb3IgdG8gc29tZXRoaW5nIG90aGVyIHRoYW4gZ3JleS4gIA0KNC4gQ2VsZWJyYXRlIQ0KDQojI0V4YW1wbGUNCg0KRm9yIG91ciBleGFtcGxlLCB3ZSBhcmUgZ29pbmcgdG8gZXhhbWluZSB0aGUgY3JpbWUgaW5jaWRlbnQgZGF0YXNldCBmcm9tIFNlYXR0bGUgOTExIENhbGxzIG9uIGRhdGEuZ292LiAgTm90ZSB0aGF0IEkgaGF2ZSBjb3ZlcmVkIHRoaXMgZGF0YSBzZXQgdGhyb3VnaCBtdWx0aXBsZSBibG9nIHBvc3RzIGFscmVhZHkgc3VjaCBhcyBtYXAgcGxvdHMgaW4gUiBhbmQgdGltZSBiYXNlZCBoZWF0IG1hcHMuICANCkluc3RhbGwgYW5kIExvYWQgTGlicmFyaWVzDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dtYXAiKQ0KI2luc3RhbGwucGFja2FnZXMoImRhdGEudGFibGUiKQ0KI2luc3RhbGwucGFja2FnZXMoImdncmVwZWwiKQ0KI2luc3RhbGwucGFja2FnZXMoImRwbHlyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpDQoNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ21hcCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KYGBgDQoNCiMjRG93bmxvYWQgdGhlIERhdGENCg0KYGBge3J9DQoNCmluY2lkZW50cz0gZnJlYWQoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sZ2VsbGlzL01pc2NUdXRvcmlhbC9tYXN0ZXIvZ2dtYXAvaTJTYW1wbGUuY3N2Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQpzdHIoaW5jaWRlbnRzKSANCmF0dGFjaChpbmNpZGVudHMpDQoNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIHNvbWUgY29sb3IgdmFyaWFibGVzIGZvciBncmFwaGluZyBsYXRlcg0KY3VzdEdyZXkgPSAiI0E5QTlBOSINCg0KI2FkZCB5ZWFyIHRvIHRoZSBpbmNpZGVudHMgZGF0YSBmcmFtZQ0KaW5jaWRlbnRzJHltZCA8LW1keV9obXMoRXZlbnQuQ2xlYXJhbmNlLkRhdGUpDQppbmNpZGVudHMkbW9udGggPC0gbHVicmlkYXRlOjptb250aChpbmNpZGVudHMkeW1kKQ0KaW5jaWRlbnRzJHllYXIgPC0geWVhcihpbmNpZGVudHMkeW1kKQ0KaW5jaWRlbnRzJHdkYXkgPC0gbHVicmlkYXRlOjp3ZGF5KGluY2lkZW50cyR5bWQsIGxhYmVsID0gVFJVRSkNCmluY2lkZW50cyRob3VyIDwtIGhvdXIoaW5jaWRlbnRzJHltZCkNCg0KI0NyZWF0ZSBhIG1vcmUgbWFuYWdlYWJsZSBkYXRhIGZyYW1lIHdpdGggb25seSAyMDE3IGRhdGENCmkyIDwtIGluY2lkZW50c1t5ZWFyPj0yMDE3LCBdDQoNCiNPbmx5IGluY2x1ZGUgY29tcGxldGUgY2FzZXMNCmkyW2NvbXBsZXRlLmNhc2VzKGkyKSwgXQ0KDQphdHRhY2goaTIpDQpoZWFkKGkyKQ0KYGBgDQoNCiMjQ3JlYXRlIGEgYmFzaWMgdGltZSBzZXJpZXMgcGxvdCBzaG93aW5nIHRoZSBjb3VudCBvZiA5MTEgZXZlbnQgdHlwZXMgYnkgbW9udGguICANCg0KYGBge3J9DQoNCiNHcm91cCB0aGUgZGF0YSBpbnRvIGEgbmV3IGRhdGEgZnJhbWUgd2hpY2ggaGFzIHRoZSBjb3VudCBvZiBldmVudHMgcGVyIG1vbnRoIGJ5IHN1Ymdyb3VwDQoNCmdyb3VwU3VtbWFyaWVzIDwtIGkyICU+JQ0KICBncm91cF9ieShtb250aCwgRXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwKSAlPiUNCiAgc3VtbWFyaXplKE4gPSBsZW5ndGgoRXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwKSkNCg0KI1ZpZXcgdGhlIG5ldyBkYXRhIHNldA0KDQpoZWFkKGdyb3VwU3VtbWFyaWVzLCBuPTEwMCkNCmF0dGFjaChncm91cFN1bW1hcmllcykNCg0KI0dyYXBoIHRoZSBkYXRhIHNldCB0aHJvdWdoIGdncGxvdCAyDQoNCmdncGxvdChncm91cFN1bW1hcmllcywgYWVzKHg9bW9udGgsIHk9TiwgY29sb3I9RXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwKSApKyANCiAgZ2VvbV9saW5lKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSJNb250aCIsIA0KICAgICAgICAgICAgICAgICBsaW1pdHM9YygzLDYsOSwxMikpDQpgYGANCg0KIyNDcmVhdGUgYSBHcmFwaCBIaWdobGlnaHRpbmcgRGF0YSB3aXRoIGEgTWF4IE1vbnRoIENvdW50IG9mIDk1IG9yIEdyZWF0ZXINCg0KYGBge3J9DQoNCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIG9ubHkgZXZlbnRzIHR5cGVzIHRoYXQgaGF2ZSBoYWQgYSBwZWFrIG9mIDk1IGNhbGxzIGluIGEgbW9udGggb3IgbW9yZQ0KDQpncm91cFN1bW1hcmllc0YgPC0gZ3JvdXBTdW1tYXJpZXMgJT4lDQogIGdyb3VwX2J5KEV2ZW50LkNsZWFyYW5jZS5TdWJHcm91cCkgJT4lIA0KICBmaWx0ZXIobWF4KE4pID4gOTUpICU+JQ0KICB1bmdyb3VwKCkNCg0KaGVhZChncm91cFN1bW1hcmllc0YpDQoNCiMgQ3JlYXRlIGEgbGF5ZXJlZCBwbG90IHdpdGggb25lIGxheWVyIG9mIGdyZXkgZGF0YSBmb3IgdGhlIGZ1bGwgZGF0YSBzZXQgYW5kIG9uZSBsYXllciBvZiBjb2xvciBkYXRhIGZvciB0aGUgc3Vic2V0IGRhdGEgc2V0IA0KDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShhZXMobW9udGgsIE4sIGdyb3VwID0gRXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwKSwgDQogICAgICAgICAgICBkYXRhID0gZ3JvdXBTdW1tYXJpZXMsIGNvbG91ciA9IGFscGhhKCJncmV5IiwgMC43KSkgKw0KICBnZW9tX2xpbmUoYWVzKG1vbnRoLCBOLCBncm91cCA9IEV2ZW50LkNsZWFyYW5jZS5TdWJHcm91cCwgY29sb3VyID0gRXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwKSwgDQogICAgICAgICAgICBkYXRhID0gZ3JvdXBTdW1tYXJpZXNGKSArICANCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0iTW9udGgiLCANCiAgICAgICAgICAgICAgICAgICBsaW1pdHM9YygzLDYsOSwxMikpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iLGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KI0hJR0hMSUdIVElORyBUSEUgTkVXIFNDSE9PTCBXQVkNCldoaWxlIHRoZSBhYm92ZSBtZXRob2RvbG9neSBpcyBxdWl0ZSBlYXN5LCBpdCBjYW4gYmUgYSBiaXQgb2YgYSBwYWluIGF0IHRpbWVzIHRvIGNyZWF0ZSBhbmQgYWRkIHRoZSBuZXcgZGF0YSBmcmFtZS4gIEZ1cnRoZXIsIHlvdSBoYXZlIHRvIHRpbmtlciBtb3JlIHdpdGggdGhlIGxhYmVsbGluZyB0byByZWFsbHkgY2FsbCBvdXQgdGhlIGhpZ2hsaWdodGVkIGRhdGEgcG9pbnRzLiAgDQoNClRoYW5rcyB0byBIaXJvYWtpIFl1dGFuaSwgd2Ugbm93IGhhdmUgdGhlIGdnaGlnaGxpZ2h0IHBhY2thZ2Ugd2hpY2ggZG9lcyBtb3N0IG9mIHRoZSB3b3JrIGZvciB1cyB3aXRoIGEgc21hbGwgZnVuY3Rpb24gY2FsbCEhICAgUGxlYXNlIG5vdGUgdGhhdCBhIGxvdCBvZiB0aGlzIGNvZGUgd2FzIGNyZWF0ZWQgYnkgbG9va2luZyBhdCBleGFtcGxlcyBvbiBoZXIgaW50cm9kdWN0aW9uIGRvY3VtZW50LiANCg0KVGhlIG5ldyBzY2hvb2wgd2F5IGlzIGV2ZW4gc2ltcGxpZXI6DQoNCjEuIFVzaW5nIGdncGxvdDIsIGNyZWF0ZSBhIHBsb3Qgd2l0aCB5b3VyIGZ1bGwgZGF0YSBzZXQuDQoyLiBBZGQgdGhlIGdnaGlnaGxpZ2h0KCkgZnVuY3Rpb24gdG8geW91ciBwbG90IHdpdGggdGhlIGNvbmRpdGlvbnMgc2V0IHRvIGlkZW50aWZ5IHlvdXIgc3Vic2V0Lg0KMy4gQ2VsZWJyYXRlISBUaGlzIHdhcyBvbmUgbGVzcyBzdGVwIEFORCB3ZSBnb3QgbGFiZWxzISANCg0KIyNFeGFtcGxlDQoNCkZvciBvdXIgZmlyc3QgZXhhbXBsZSwgd2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSB0aGUgc2FtZSB0aW1lIHNlcmllcyBncmFwaCBmcm9tIGFib3ZlLiAgSG93ZXZlciwgd2UgYXJlIGdvaW5nIHRvIHBlcmZvcm0gdGhlIGhpZ2hsaWdodGluZyB3aXRoIGdnaGlnaGxpZ2h0IHZzIG1hbnVhbCBsYXllcmluZy4gIA0KDQpgYGB7cn0NCiMgSW5zdGFsbCB0aGUgZ2doaWdobGlnaHQgcGFja2FnZQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiZ2doaWdobGlnaHQiKQ0KI2xpYnJhcnkoZ2doaWdobGlnaHQpDQoNCiMgQ3JlYXRlIHRoZSBoaWdobGlnaHRlZCBncmFwaA0KDQpnZ3Bsb3QoZ3JvdXBTdW1tYXJpZXMsIGFlcyhtb250aCwgTiwgY29sb3VyID0gRXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwKSkgKw0KICBnZW9tX2xpbmUoKSArIA0KICBnZ2hpZ2hsaWdodChtYXgoTikgPiA5NSwgIGxhYmVsX2tleSA9IEV2ZW50LkNsZWFyYW5jZS5TdWJHcm91cCkgKyAgDQogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9Ik1vbnRoIiwgDQogICAgICAgICAgICAgICAgICAgbGltaXRzPWMoMyw2LDksMTIpKQ0KYGBgDQoNCiMjTW9yZSBFeGFtcGxlcw0KDQpXZWxsIHRoYXQgd2FzIHNvIGVhc3ksIHdlIGFyZSBnb2luZyB0byB0cnkgYSBmZXcgbW9yZSBnZ21hcCBwbG90IHR5cGVzIHRvIHNlZSBob3cgd2UgZmFyZS4gIEJlbG93IHNob3cgYm90aCBhIHNjYXR0ZXJwbG90IGFuZCBoaXN0b2dyYW0gY2hhcnQuICANCg0KYGBge3J9DQojIFRyeSBhIHNjYXR0ZXJwbG90IGNoYXJ0DQoNCmdncGxvdChncm91cFN1bW1hcmllcywgYWVzKG1vbnRoLCBOLCBjb2xvdXIgPSBFdmVudC5DbGVhcmFuY2UuU3ViR3JvdXAsIHVzZV9ncm91cF9ieT1GQUxTRSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2doaWdobGlnaHQoTiA+IDIwMCwgIGxhYmVsX2tleSA9IEV2ZW50LkNsZWFyYW5jZS5TdWJHcm91cCkgKyANCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0iTW9udGgiLA0KICAgICAgICAgICAgICAgICAgIGxpbWl0cz1jKDMsNiw5LDEyKSkNCg0KIyBUcnkgYSBoaXN0b2dyYW0gY2hhcnQNCg0KZ2dwbG90KGdyb3VwU3VtbWFyaWVzLCBhZXMoTiwgZmlsbCA9IEV2ZW50LkNsZWFyYW5jZS5TdWJHcm91cCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIixsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArDQogIGdnaGlnaGxpZ2h0KE4gPiAxMDAsICBsYWJlbF9rZXkgPSBFdmVudC5DbGVhcmFuY2UuU3ViR3JvdXAsIHVzZV9ncm91cF9ieSA9IEZBTFNFKSArDQogIGZhY2V0X3dyYXAofiBFdmVudC5DbGVhcmFuY2UuU3ViR3JvdXApDQpgYGANCg0K